Separation of API & Web Auth Guard Concerns
In this lesson, we'll restrict our routes to their applicable authentication guard. Ensuring our web routes can properly authorize using their role-based authorization and our API can properly authorize using our access token abilities.
- Author
- Tom Gobich
- Published
- Apr 21
- Duration
- 9m 17s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.
Adocasts
Burlington, KY
Get the Code
Download or explore the source code for this lesson on GitHub
Transcript
Separation of API & Web Auth Guard Concerns
-
Now, at the end of the day,
-
there's some things that we want our web authenticated users
-
to be able to do within our web dashboard
-
that we don't want our API authenticated users
-
to be able to do.
-
For example, within our organization settings,
-
we're not going to want our API authenticated users
-
to be able to manage or invite users
-
inside of the organization itself.
-
We want that restricted to just this web dashboard.
-
Since that is an admin specific functionality,
-
but our access tokens are going to be
-
for the organization itself
-
that doesn't have one of those user-based roles.
-
So to easily keep the separation of concerns
-
between those different routes for our web application
-
and then our API application,
-
what we can do is jump into our start.
-
And then inside of this application,
-
I have a route split between a couple of different files
-
here between our auth and currently our web.
-
And then we'll add an additional one here
-
for our API as well.
-
So if we take a look at our web routes,
-
for example, we have our authentication middleware
-
being used as a group against a number of different routes.
-
As we have it here,
-
all that this cares about is that something is authenticated.
-
In this case, that could be our user
-
or it could be our organization
-
through our API access tokens.
-
So in order to keep that separation of concerns
-
that we had talked about,
-
we want to limit this auth middleware
-
to specifically check just our web guard
-
for our authentication.
-
So we can pass some options into this.
-
And one of these options is guards.
-
And this accepts in an array of guards
-
that we want this authentication to check against.
-
So we'll specify web in there.
-
And now this will only authorize access to these routes
-
if a user's authenticated specifically
-
with the web authentication guard.
-
If an API user tries to reach one of these routes,
-
they're gonna get an access denied error
-
or redirected away depending on content negotiation.
-
So we also have that being applied down here at the bottom
-
for the main set of these web routes as well right here.
-
So we wanna do that here too.
-
So we'll add guards and specify
-
that this should only accept in the web guard.
-
Our auth routes are mostly going to just be checking
-
whether or not the user's a guest,
-
which is a non-authenticated user.
-
So we don't need to specify it on there,
-
but we do have this one here for our logout
-
that we could add a guard specificity on as well.
-
So we'll add guards here and specify
-
that that one should only be applicable to web.
-
Since the way that we're going to be using
-
these access tokens,
-
there's not really going to be a logout mechanism.
-
So with those set,
-
we now have a separation of concerns between our routes
-
and which auth guard they should actually use
-
and authorize access to for those routes.
-
Then we can go ahead and add a new route in
-
for these API routes that we'll be adding.
-
So we'll do api.ts,
-
and let's go ahead and give ourselves a router.
-
Be sure to import the router
-
from AdonisJS Core Services router,
-
not the one from Inertia,
-
and get ourselves a new group going on here.
-
Inside of this group is where we will define our API routes.
-
We don't have any defined at this moment,
-
so we'll just add that comment in there,
-
and then we'll have this use a couple of middleware.
-
So first and foremost,
-
we want this to use our auth middleware.
-
So we'll call auth,
-
and we want this to specifically check the API guard
-
for that, limiting web authenticated users
-
from these routes.
-
Now, in addition to that,
-
there's also a middleware that pre-fills our organization
-
onto the HTTP context.
-
We'll keep that middleware intact,
-
but we do need to alter it a little bit
-
to work correctly with our API access tokens.
-
So we'll alter that here in a little bit,
-
but there's actually a middleware
-
that we want to create as well
-
that will force the content negotiation for our API routes
-
to be application JSON.
-
So let's do node ace make middleware
-
within our terminal here,
-
and we'll call this force JSON response,
-
and we'll have this be a named middleware
-
as we don't want it applied to our web routes.
-
All right, we can clear our terminal out there,
-
jump back into our text editor,
-
and let's go ahead and add that
-
into this route group as well.
-
So middleware.forceJSONResponse.
-
I'm gonna go ahead and put it first here
-
so that the response is already in place for our auth guard
-
as well as our organization guard
-
in case anything should either error out
-
or come back as unauthorized in either one of those.
-
Now we have a checklist currently,
-
have a couple of things that we need to do.
-
We need to update our organization middleware
-
to handle our API.
-
We haven't yet filled out
-
our force JSON response middleware,
-
and we also need to register this new API route file
-
within our applications preloads.
-
So we'll do that in the inverse order that I just listed.
-
Let's go ahead and start by jumping down
-
into our adonisrc.ts file.
-
This is where we add in the preloads
-
that we want to be loaded in
-
during the boot cycle of our application.
-
You can see that we already have
-
our other two route files defined here.
-
Had I jumped into the ACLI and run node ace make preload,
-
this would have automatically been registered for me,
-
but since I just right-clicked the file tree
-
and created a file, we need to manually add it in here.
-
So it's gonna be start routes/api, just like so.
-
And now our API file will be loaded along with our routes
-
whenever our application boots up.
-
Okay, let's close that out.
-
And next, let's go ahead and fill out
-
our force JSON response middleware.
-
So let's go up to our middleware here,
-
where we have our force JSON response middleware.
-
We can go ahead and just get rid of everything
-
that's in here so far.
-
And then from our HTTP context,
-
let's go ahead and extract out the request.
-
And what we wanna do with this middleware
-
is exactly as the name suggests,
-
force the response for the content negotiation
-
of the request to be application JSON.
-
So to do that, let's go ahead and grab the current headers
-
from the request.
-
So request.headers.
-
This is gonna give us back an object
-
of type incoming HTTP headers.
-
Content negotiation goes off of the accept header.
-
So if we overwrite this and force it
-
to accept application JSON,
-
that's gonna force the response type
-
via content negotiation for this request
-
to be application JSON,
-
fulfilling what we're after here for this middleware.
-
Once we have that set,
-
we can go ahead and continue onward
-
with the middleware tree by returning next.
-
Next is the function that when called
-
allows AdonisJS to move on from this middleware
-
to the next one or to the controller as a whole.
-
All right, we can close that one out now as well.
-
And then that leaves us with our organization middleware
-
that we need to update.
-
So let's jump into there next.
-
We need to update this to handle our API.
-
Everything in place here so far
-
is specific to our web application.
-
It's going to get the active organization
-
off of the user's cookies,
-
pre-fill the user's role inside of that organization,
-
and then add that onto the HTTP context
-
along with their abilities based off of the role.
-
Then it will add it into Inertia's data
-
to share with our front-end application.
-
So we can bypass all of that
-
if we are inside of an API request.
-
And with how we have our authentication
-
separation of concerns,
-
it's actually really easy to discern
-
whether or not we're in an API request for this.
-
If we have an API authenticated user,
-
then we want to bypass all of this
-
and instead use the authenticated organization
-
as the organization that we populate onto the HTTP context.
-
Otherwise, we can assume that it's a web authenticated user
-
and have this middleware remain as is.
-
So at the start of this handle method,
-
we'll just do an if ctx auth use API.user.
-
So if our API guard has an authenticated user bound to it,
-
then we know that we have a user authenticated via our API.
-
So we can do ctx.organization equals ctx auth use API.user
-
and assert that that user's there.
-
Since we're specifying that we're using the API guard,
-
our user is going to be of type,
-
ah, where is it here?
-
Organization, and then it's also going to bind
-
in that current access token that that organization has.
-
And that type is as defined inside of our API guard
-
within our auth configuration.
-
So despite this saying user,
-
this is actually our organization authenticated with our API.
-
Once we've populated our HTTP context's
-
organization property,
-
we can go ahead and continue onward with the middleware tree
-
by returning the next function.
-
That will bypass everything else down here
-
since we've now returned,
-
and next will allow the middleware tree to continue onward
-
from this organization middleware.
-
If we don't have an API authenticated user,
-
then it will continue onward with this middleware
-
as is usual.
-
So that is all now a-okay and ready to go.
-
If you're not coming from our building
-
with AdonisJS and Inertia series,
-
what this is doing is adding the organization
-
onto our HTTP context.
-
Further down on this page,
-
we have the type augmentation for that,
-
where we're extending the HTTP context
-
and adding on the organization right here.
-
Oh, looks like we also have an organization ID there,
-
which we are, yep, we're populating right there.
-
So we can go ahead and populate that for our API as well.
-
CTX organization ID equals,
-
and we just do CTX organization.ID
-
to simplify the reach for the organization there.
-
And then throughout our controllers,
-
so if I scroll on up here,
-
go into our controllers,
-
and let's go into, say, our courses controller,
-
we're then able to extract the organization
-
directly out of our HTTP context to use as needed.
-
So that's what we're doing inside of this middleware,
-
just grabbing the requests organization
-
and pre-populating that onto the HTTP context.
-
Introduction
-
API Authentication
-
2.0Configuring Access Token Auth on top of Session AuthLesson 2.010m 50s
-
2.1Separation of API & Web Auth Guard ConcernsLesson 2.19m 17s
-
2.2Defining Access Token Abilities & DTOLesson 2.29m 54s
-
2.3Creating Access Tokens Part 1: AdonisJSLesson 2.310m 19s
-
2.4Creating Access Tokens Part 2: Inertia/VueLesson 2.412m 44s
-
2.5Opaque Access Tokens (OAT) vs JSON Web Tokens (JWT)Lesson 2.53m 57s
-
Listing an Organization's Access TokensLesson 2.68m 3s
-
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!