00:02
Now, at the end of the day,
00:06
there's some things that we want our web authenticated users
00:08
to be able to do within our web dashboard
00:11
that we don't want our API authenticated users
00:13
to be able to do.
00:14
For example, within our organization settings,
00:17
we're not going to want our API authenticated users
00:20
to be able to manage or invite users
00:23
inside of the organization itself.
00:25
We want that restricted to just this web dashboard.
00:27
Since that is an admin specific functionality,
00:30
but our access tokens are going to be
00:32
for the organization itself
00:33
that doesn't have one of those user-based roles.
00:36
So to easily keep the separation of concerns
00:38
between those different routes for our web application
00:41
and then our API application,
00:43
what we can do is jump into our start.
00:46
And then inside of this application,
00:47
I have a route split between a couple of different files
00:50
here between our auth and currently our web.
00:52
And then we'll add an additional one here
00:54
for our API as well.
00:55
So if we take a look at our web routes,
00:57
for example, we have our authentication middleware
00:59
being used as a group against a number of different routes.
01:03
As we have it here,
01:04
all that this cares about is that something is authenticated.
01:08
In this case, that could be our user
01:09
or it could be our organization
01:11
through our API access tokens.
01:13
So in order to keep that separation of concerns
01:15
that we had talked about,
01:16
we want to limit this auth middleware
01:17
to specifically check just our web guard
01:20
for our authentication.
01:22
So we can pass some options into this.
01:23
And one of these options is guards.
01:26
And this accepts in an array of guards
01:28
that we want this authentication to check against.
01:31
So we'll specify web in there.
01:33
And now this will only authorize access to these routes
01:36
if a user's authenticated specifically
01:38
with the web authentication guard.
01:40
If an API user tries to reach one of these routes,
01:43
they're gonna get an access denied error
01:44
or redirected away depending on content negotiation.
01:47
So we also have that being applied down here at the bottom
01:50
for the main set of these web routes as well right here.
01:53
So we wanna do that here too.
01:55
So we'll add guards and specify
01:57
that this should only accept in the web guard.
02:00
Our auth routes are mostly going to just be checking
02:03
whether or not the user's a guest,
02:05
which is a non-authenticated user.
02:07
So we don't need to specify it on there,
02:08
but we do have this one here for our logout
02:11
that we could add a guard specificity on as well.
02:14
So we'll add guards here and specify
02:16
that that one should only be applicable to web.
02:18
Since the way that we're going to be using
02:20
these access tokens,
02:20
there's not really going to be a logout mechanism.
02:23
So with those set,
02:25
we now have a separation of concerns between our routes
02:28
and which auth guard they should actually use
02:30
and authorize access to for those routes.
02:33
Then we can go ahead and add a new route in
02:35
for these API routes that we'll be adding.
02:37
So we'll do api.ts,
02:39
and let's go ahead and give ourselves a router.
02:41
Be sure to import the router
02:42
from AdonisJS Core Services router,
02:44
not the one from Inertia,
02:46
and get ourselves a new group going on here.
02:48
Inside of this group is where we will define our API routes.
02:52
We don't have any defined at this moment,
02:54
so we'll just add that comment in there,
02:55
and then we'll have this use a couple of middleware.
02:58
So first and foremost,
02:59
we want this to use our auth middleware.
03:02
So we'll call auth,
03:04
and we want this to specifically check the API guard
03:08
for that, limiting web authenticated users
03:11
from these routes.
03:12
Now, in addition to that,
03:13
there's also a middleware that pre-fills our organization
03:16
onto the HTTP context.
03:18
We'll keep that middleware intact,
03:20
but we do need to alter it a little bit
03:22
to work correctly with our API access tokens.
03:24
So we'll alter that here in a little bit,
03:27
but there's actually a middleware
03:27
that we want to create as well
03:30
that will force the content negotiation for our API routes
03:33
to be application JSON.
03:36
So let's do node ace make middleware
03:38
within our terminal here,
03:40
and we'll call this force JSON response,
03:43
and we'll have this be a named middleware
03:46
as we don't want it applied to our web routes.
03:48
All right, we can clear our terminal out there,
03:50
jump back into our text editor,
03:51
and let's go ahead and add that
03:53
into this route group as well.
03:54
So middleware.forceJSONResponse.
03:57
I'm gonna go ahead and put it first here
03:58
so that the response is already in place for our auth guard
04:02
as well as our organization guard
04:03
in case anything should either error out
04:06
or come back as unauthorized in either one of those.
04:09
Now we have a checklist currently,
04:10
have a couple of things that we need to do.
04:11
We need to update our organization middleware
04:13
to handle our API.
04:15
We haven't yet filled out
04:16
our force JSON response middleware,
04:18
and we also need to register this new API route file
04:22
within our applications preloads.
04:24
So we'll do that in the inverse order that I just listed.
04:26
Let's go ahead and start by jumping down
04:28
into our adonisrc.ts file.
04:30
This is where we add in the preloads
04:33
that we want to be loaded in
04:35
during the boot cycle of our application.
04:37
You can see that we already have
04:38
our other two route files defined here.
04:40
Had I jumped into the ACLI and run node ace make preload,
04:44
this would have automatically been registered for me,
04:46
but since I just right-clicked the file tree
04:48
and created a file, we need to manually add it in here.
04:50
So it's gonna be start routes/api, just like so.
04:54
And now our API file will be loaded along with our routes
04:58
whenever our application boots up.
04:59
Okay, let's close that out.
05:01
And next, let's go ahead and fill out
05:03
our force JSON response middleware.
05:04
So let's go up to our middleware here,
05:07
where we have our force JSON response middleware.
05:09
We can go ahead and just get rid of everything
05:11
that's in here so far.
05:12
And then from our HTTP context,
05:13
let's go ahead and extract out the request.
05:16
And what we wanna do with this middleware
05:19
is exactly as the name suggests,
05:21
force the response for the content negotiation
05:23
of the request to be application JSON.
05:26
So to do that, let's go ahead and grab the current headers
05:29
from the request.
05:30
So request.headers.
05:31
This is gonna give us back an object
05:33
of type incoming HTTP headers.
05:35
Content negotiation goes off of the accept header.
05:39
So if we overwrite this and force it
05:42
to accept application JSON,
05:44
that's gonna force the response type
05:45
via content negotiation for this request
05:48
to be application JSON,
05:49
fulfilling what we're after here for this middleware.
05:52
Once we have that set,
05:52
we can go ahead and continue onward
05:54
with the middleware tree by returning next.
05:56
Next is the function that when called
05:58
allows AdonisJS to move on from this middleware
06:01
to the next one or to the controller as a whole.
06:04
All right, we can close that one out now as well.
06:07
And then that leaves us with our organization middleware
06:10
that we need to update.
06:11
So let's jump into there next.
06:12
We need to update this to handle our API.
06:15
Everything in place here so far
06:17
is specific to our web application.
06:19
It's going to get the active organization
06:21
off of the user's cookies,
06:23
pre-fill the user's role inside of that organization,
06:26
and then add that onto the HTTP context
06:28
along with their abilities based off of the role.
06:31
Then it will add it into Inertia's data
06:34
to share with our front-end application.
06:35
So we can bypass all of that
06:38
if we are inside of an API request.
06:41
And with how we have our authentication
06:43
separation of concerns,
06:45
it's actually really easy to discern
06:47
whether or not we're in an API request for this.
06:50
If we have an API authenticated user,
06:52
then we want to bypass all of this
06:54
and instead use the authenticated organization
06:57
as the organization that we populate onto the HTTP context.
07:01
Otherwise, we can assume that it's a web authenticated user
07:05
and have this middleware remain as is.
07:07
So at the start of this handle method,
07:08
we'll just do an if ctx auth use API.user.
07:15
So if our API guard has an authenticated user bound to it,
07:19
then we know that we have a user authenticated via our API.
07:22
So we can do ctx.organization equals ctx auth use API.user
07:27
and assert that that user's there.
07:32
Since we're specifying that we're using the API guard,
07:35
our user is going to be of type,
07:37
ah, where is it here?
07:38
Organization, and then it's also going to bind
07:40
in that current access token that that organization has.
07:44
And that type is as defined inside of our API guard
07:47
within our auth configuration.
07:49
So despite this saying user,
07:50
this is actually our organization authenticated with our API.
07:55
Once we've populated our HTTP context's
07:57
organization property,
07:58
we can go ahead and continue onward with the middleware tree
08:01
by returning the next function.
08:04
That will bypass everything else down here
08:06
since we've now returned,
08:07
and next will allow the middleware tree to continue onward
08:11
from this organization middleware.
08:13
If we don't have an API authenticated user,
08:16
then it will continue onward with this middleware
08:19
as is usual.
08:20
So that is all now a-okay and ready to go.
08:23
If you're not coming from our building
08:24
with AdonisJS and Inertia series,
08:26
what this is doing is adding the organization
08:29
onto our HTTP context.
08:31
Further down on this page,
08:32
we have the type augmentation for that,
08:34
where we're extending the HTTP context
08:36
and adding on the organization right here.
08:38
Oh, looks like we also have an organization ID there,
08:41
which we are, yep, we're populating right there.
08:43
So we can go ahead and populate that for our API as well.
08:46
CTX organization ID equals,
08:49
and we just do CTX organization.ID
08:52
to simplify the reach for the organization there.
08:55
And then throughout our controllers,
08:57
so if I scroll on up here,
08:59
go into our controllers,
09:00
and let's go into, say, our courses controller,
09:02
we're then able to extract the organization
09:04
directly out of our HTTP context to use as needed.
09:08
So that's what we're doing inside of this middleware,
09:10
just grabbing the requests organization
09:13
and pre-populating that onto the HTTP context.
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!