Developing a Secure API with NestJS

One thing to note – it took me a while to figure out that the WHATABYTE thing is a custom application build specifically for the demo – is that mentioned and I missed it?

Glad to hear it’s working! The demo app is introduced in the “Getting Started” chapter and then it’s later explained that it’s a client used to mimic a real login instead of using a terminal and sending a token manually in the “Managing Identity” chapter.

I am refreshing the content and I really like this. I am going to use it :eyes: I’ll give you a shout-out if it makes it past review :muscle:

1 Like

This is going into the next update of this tutorial :muscle: Thank you again for bringing it up.

I very much want to add this to the next update. Doing R&D on that now :eyes: Great suggestion and thank you again. I love to use Rules to automate things like this :robot:

@mob I found a challenge on this. I think that guards are executed in order in NestJS. If I wrap the controller in the @UseGuards(PermissionsGuard) it fails because the AuthGuard is the one that injects the user object into the Execution Context. But AuthGuard would be called at the endpoint-level – after
PermissionsGuard.

I tried your other suggestion on modeling the PermissionsGuard to take the required permissions as parameter but that fails unless I remove the @Injectable() from the PermissionsGuard definition and when the guard is used, I always have to start it with new. I re-read some of the docs and it seems to me that NestJS wants you do specify this type of metadata (permimissions required, roles, etc.) outside of the guard and rather have the guard consume it. :thinking: What do you think?

Well… I think that’s too bad :smiley:

I suppose you cannot wrap the controller with @UserGuards(AuthGuard('jwt'), PermissionsGuard) since some endpoints could be public or something?

Nonetheless, I found it strange that passing params to the PermissionsGuard fails since the AuthGuard does have one :thinking:
I’ve look at your tutorial again, specifically the code of the PermissionsGuard, and I thought you could use a factory function to provide the Guard instance to @UseGuards instead of passing the PermissionsGuard class.
And this is indeed what @nestjs/passport does for the AuthGuard, which accepts arguments (See the source code). Although there’s a little bit more complexity in their case (they export an AuthGuard function that uses some TypeScript sorcery which in turn calls the factory function), in the case of the permissions, I think that could be simplified.
Might be worth a try. Or maybe it’s a little bit too advanced for this tutorial? I dunno :man_shrugging:

Yes! Exactly! Yesterday, after seeing how to pass parameters to Middleware functions in NestJS, I tried the same with Guards but it was not working well, I was getting compiler errors. I went to look into the source code of AuthGuard and found it follows a “factory” / “mixin” pattern. The key is using a mixin methods from @nestjs/common that I can’t find documentation on. So, I got it working now. :smiley:

There are a few things in NestJS that help with common Express patterns but that are not documented.

My new version of this tutorial that I submitted for review is…

  • Dropping Passport.
  • Use an AuthGuard instead that verifies the access token present in the request.
  • Dropping decorator.
  • Use a PermissionsGuard that takes as argument the permissions the route requires.
  • Testing the API without using a client frontend-application :scream: Adios, WAB Dashboard :eyes:
  • Use ConfigService instead of dotenv / process.env to access .env variables.

I feel that using Middleware for both authorization and permissions checking ends up becoming a not-so-good developer experience as now you have to bloat up a Nest module with protection logic and maintain two files: module and controller. If you look at the controller, you wouldn’t know if an endpoint is protected, you’d have to find the middleware for the path.

Instead of doing that, you can have the same contextual information and function by using Guards on the controller or endpoint.

2 Likes

Glad to ear you’ve worked it out!
And I’m curious to read your new tutorial version — quite the changes you’re annoucing! :muscle:

2 Likes

Got you covered @mob!

@dan-auth0 can I be more specific around my request for rules. I have setup the “Add user roles from a SQL Server database” which applies the user’s role into a roles array in the tokens. It does not, however, add the scopes (e.g. read:data) into the permissions array on the access_token.

If I add the role to the user through the Auth0 dashboard, the scopes are added as permissions as expected.

I am probably missing something, and I realise I can use the Auth0 Management API to allocate/deallocate roles to users. Maybe this is the answer.

It just seems elegant to apply the roles during login via rules, and that was my request.

Cheers.

Hey guys I came here from the article as well. Interesting discussion.
I’m stuck on a part of the role concept; my app has a whole bunch of tenants and a user is supposed to have all kinds of permission for each tenant. So i could duplicate the roles and permissions for each tenant but that seems like a bad idea to me. I’m wondering if it’s possible at all to model permissions like that in auth0 or if I should save the data myself as it’s also possible to change it from within the app.

1 Like

Hello, Lukas. Welcome to our Community. Let me ask our team about it. I just want to clarify: are you asking if it’s possible to define the roles and permissions in a centralized way at Auth0 and have different tenants access those definitions?

Gotcha! What kind of flow do you have in mind?
(1) User logs in
(2) Rules kick in
(3) Roles are added to the access token
(4) Permissions are added automatically to the token based on the roles from (3)

I think what you are saying is that when the roles are added programmatically, the RBAC feature of including the permissions associated with each role does not kick in, correct?

Unfortunately I am also having trouble with the example code, and always getting the 401 error. My code is setup exactly as in the tutorial. I have a frontend React app which gets a token using getTokenSilently() and sends it as an Authorization header (Bearer: ${token}) to my server.

But the validate() function never gets called. The controller works when the AuthGuard is removed, otherwise just returns 401. Would anyone have any ideas for what to try? Thank you!

1 Like

Howdy, Sarim! There’s a big update coming soon. I figured that using Passport.js for this use case was definitely not the best approach :eyes: I am going to be sharing with y’all here a new approach that I feels is more idiomatic.

Currently is going under review :microscope: Next week, I’ll be pushing the updates to this tutorial. On the meantime, are you using the Issuer URL or the domain?

Thank you for your response Dan! I was actually a bit confused about that. My tenant name is: XY. Which is also embedded in the domain listed in the settings for the demo client: XY.auth0.com. So in my .env file, I have AUTH0_DOMAIN=https://XY.auth0.com/, and this is what I use within the strategy:

// jwt.strategy.ts
jwksUri: `${process.env.AUTH0_DOMAIN}.well-known/jwks.json`
issuer: `${process.env.AUTH0_DOMAIN}`,

So it seems like everything is set correctly. But I’m happy to wait until next week for the revised tutorial! I can work on another part of my app until then. Thank you!

2 Likes

No worries! We’re here for you @sarimabbas!

That looks fine, Sarim. Did you log out and log in again after you gave the user the role? :thinking:

Thank you for the tutorial; it was really helpful. As you mentioned in the end that you are planning to add these three chapters:

  • Deploying a NestJS application to Heroku and AWS.
  • Connecting a NestJS application to a MongoDB or PostgreSQL store.
  • Using GraphQL or gRPC with NestJS.
    So, please add them as soon as possible because I want to learn how to develop a complete solution. One request is that along with MongoDB please add another chapter on Mongoose ODM because that helps in mapping databases functions to the backend code (NestJS).