Hi everybody !
Today, I’m gonna summarize key aspects of implementing guards with Nest.js and Passport. They will be discussed in Express context because… huh well, I haven’t done any Fastify in a couple of years 😅
Please note that if you want to jump straight up to the code and related integration tests, you can have a look at the related sample repo directly :
This article is an extension of one I previously wrote here, it assumes you already have a good understanding of Nest.js, Passport, Express and Typescript decorators. If you don’t already, please be sure to have a look at the following resources first.
Documentation | NestJS - A progressive Node.js framework
A guard is a class annotated with the @Injectable() decorator. Guards should implement the CanActivate interface…
Passport is authentication middleware for Node. It is designed to serve a singular purpose: authenticate requests. When…
Express is a routing and middleware web framework that has minimal functionality of its own: An Express application is…
Is this useful to me ?
If you’re wondering how to :
- use injection in guard / strategy
- implement multiple strategies
- implement multiple guards
Yeah, then this is for you 😃
Let’s dive in ! 💻
First, let’s have a quick refresher on both
In Nest.js, here is the order in which decorators get called :
- Controller route handler method
Hence, for example, don’t expect a 400 / Validation failed error with a malformed payload if ever your request does not pass the Guard in the first place.
To put it simply, it does :
- extends Guard
- pipe the options to the underlying passport strategy
- call the authorization logic of the underlying passport strategy
- finally call
verifymethod (which, under the hood, calls
validateon Nest.js PassportStrategy), allowing you to plug your own additional authorization logic
The golden rule to remember : multiple strategies will succeed as long as at least one passes.
Typical use case include providing alternate ways to authorize requests.
Let’s implement both a JWT strategy for the Bearer request header and a JWT strategy for the request query (which will retrieve the JWT from a
token query parameter) :
As you can notice, the only thing that differ between these 2 classes is the
jwtFromRequest option that is used with
passport-jwt strategy and the name given to the strategies (here
jwt.query), so you could of course refactor it in a nicer way.
Then, to use them all you have to do is create the corresponding
And use it in your controller :
This way, as long as a valid JWT is sent either in the expected header or query parameter, you user will be allowed by the
The golden rule to remember : multiple guards will succeed as long as all of them passes.
Typical use cases include reusing different combinations of Guard(s) on different Controller routes.
Let’s say we have 2 routes, both expect a valid JWT to be provided but one requires the user to be an admin while the other one implement some kind of IP rate limiting.
Of course for rate limiting you would typically use some nice library like nestjs-rate-limiter but for the purpose of this example I will use a dummy homemade implementation (please don’t use this in production).
Let’s first assume we have this controller :
I’m gonna reuse the
JwtGuard from the former example, and add on top of it :
RoleGuard is nothing more than the one depicted in Nest.js documentation : it checks that the authenticated user has the required role.
RateGuard will save request IP and compare it with previous ones : provided the
limit has been reached, it will prevent the request from completing successfully.
Here again, you can find the details of the associated services and decorators implementation in the sample repo.
Context & injection in guard / strategy
I’ve often seen and wondered myself what I could actually use or inject in
- Guard has access to both
Reflector, so it can retrieve metadata from Controller’s route handler
- Strategy can only access
- Strategy can be injected with any service (or provider, as Nest.js also call them)
- Guard is not meant to be injected with any service
What if I need both ?
There’s a way :
- retrieve the metadata in the Guard
- store them on the request before triggering strategy from
- then retrieve them from the request in Strategy’s
validateand perform your logic with injected services
Seems like a weird solution ?
That’s actually what passport already does with req.user 👈
That’s actually what I’ve done for the
RATE_LIMITis defined on the Controller route
this way a different rate limit could be set for different routes
- it is then retrieved in the
RateGuardand set on the request
request.limit = limit
- finally it’s retrieved from the request in the
RateStrategyand compared to check whether to allow the request or not
I hope it will be useful to you and save you some time digging into the documentation / stackoverflow / source code 😃
Stay safe and take care of your relatives !
Please find the sample repo with integration tests here :