Developing a Secure API with NestJS

Learn how to use NestJS, a Node.js framework powered by TypeScript, to build a secure API.

Read on :cat2:

Brought to you by @dan-auth0 :man_technologist:t2:

3 Likes

Iā€™m loving TypeScript. NestJS made it much easier to integrate it within the context of NodeJS. Let me know what you think about this tutorial, please :pray: Loved it? Hated it?

@dan-auth0 This seems like a great tutorial! Iā€™ve been meaning to try NestJS for quite some time now, and this article looks like a very good entry point.

Thereā€™s one thing I donā€™t understand though, and that is need to create the @Permissions decorator.
In the final example, you use it like this:

@UseGuards(AuthGuard('jwt'), PermissionsGuard)
@Post()
@Permissions('create:items')
create(@Body('item') item: Item) {
  this.itemsService.create(item);
}

But I really donā€™t see the added value of the @Permissions (other than the fact that I like decorators :sweat_smile:) . Is there any particular reason why you didnā€™t pass the permissions to PermissionsGuard through an argument like itā€™s done for the AuthGuard?

@UseGuards(AuthGuard('jwt'), PermissionsGuard('create:items'))
@Post()
create(@Body('item') item: Item) {
  this.itemsService.create(item);
}

That would avoid the creation of a new decorator. Did I miss something?

But I really like the syntax simplicity of the @Permission decorator :smiley:
Wouldnā€™t it be possible instead to declare the PermissionGuard to be used globally for all methods (since it doesnā€™t prevent access to method without attached permissions anyway)?
This way, we wouldnā€™t have to add the guard to methods that do have the @Permission decorator, like this:

@UseGuards(AuthGuard('jwt'))
@Post()
@Permissions('create:items')
create(@Body('item') item: Item) {
  this.itemsService.create(item);
}

Anyway, kudos again for this tutorial. Iā€™ll surely do more than just read it sometime :+1:

1 Like

Thank you for reading and for your feedback :slight_smile:

To address your question:

Is there any particular reason why you didnā€™t pass the permissions to PermissionsGuard through an argument like itā€™s done for the AuthGuard ?

Itā€™s for educational purposes as to how to create a custom decorator. Both methods would work well. I do prefer the @Permissions() decorator as it reads so cleanly but also could be used by something different than the guard, perhaps :thinking:

Great tutorial. I had already implemented the same integration (Auth0 via NestJS) - but a bit differently. Wanted to point out something and ask a couple questions:

This may be an inconsistency in code vs description:

The PermissionGuard has:

    const hasPermission = () =>
      userPermissions.some(permission => routePermissions.includes(permission));

Then the copy states:
ā€œThe route handler has permissions set and each one of them is present in the permissions claim of the access token.ā€

userPermissions.some will only require that one of the permissions is met, as opposed to ā€œeach one of themā€. The code should probably be userPermissions.every or the copy should be ā€œone of N permissions metā€. (To this end - I am curious which approach is ā€œbetterā€ - every or some - or perhaps supporting both via separate decorators is a good thing)

Questions:

  1. In my implementation, I call the Auth0 API to get the userā€™s current permissions, then cache the results of that API call for N (10 minutes) - as opposed to storing permissions in accessToken and just reading it. Should I swap to storing/reading in accessToken? If you use accessToken, how do you handle permission changes when tokens are out in the wild?

  2. In the docs here it is stated that access tokens should never be used for authentication. The tutorial states:
    ā€œThen, the access token can be sent as a bearer token in an authorization header to prove authentication to the server on requests made to protected endpoints.ā€
    This seems in contradiction to the docs I linked stating access token should not be used for AuthN. Could you help clarify?

  3. Related to #2 - is it best a best practice to authenticate with idToken and then authorize with accessToken? IE check authentication via idToken in middleware, then confirm permissions by checking accessToken in a guard? That was my understanding; seems like the tutorial only does AuthZ, but not AuthN.

  4. ā€œRefresh the WAB Dashboard to rotate the access tokenā€ - where is the access token rotation implemented?

  5. Is there a repo somewhere showing the full implementation?

Thanks in advance.

1 Like

Howdy! Welcome to the Auth0 community and thank you for reading this blog post :slight_smile: I apologize for the delay in response. I was on :beach_umbrella: vacation. Iā€™ll be working a response for you within the next days :pray:

2 Likes

Hello.

I have implemented almost the same solution and would be happy to help with the next chapters and review.

To be hones an article like this would have been a tremendous help for me a couple of month ago, so thank you very much for writing it. It will be really helpful for the community!

The client side code would be interesting to go through. How you implemented token refresh, audience handling etc.

Ideas for the article mixed with a couple of questions:

  • I have not used passport but express-jwt and a middleware. I believe passport is an overshoot for this task and makes life a bit more complex then it should be. If a middleware is used with a custom jwt validation (which is done in the startegy anyway) to store the jwt on the req object and a single AuthGuard is created then it would remove a bit of magic from the flow and 1 guard would be enough instead of 2
  • Using guards would be problematic with graphql in my opinion, because every ResolvePropery should be protected anywhere it is used. Because of this I have put the authorization logic in the service layer.
  • On the client side itā€™s ā€œtrickyā€ to know how to force auth0 to use jwt access_token-s (audience has to be specified)
  • It would be greate to know what is the best practice if I have multiple api-s? How do I handle it on the UI? (The ui should request the access token? Or the back-end?)
  • The jwt parse is a bit simplisitic. The revoked token handling is not implemented I believe
  • express-jwt is used in all the old docs and in the example code but itā€™s not really maintained
  • the client callback has a bit of black magic to it (dashboard.whatabyte.now.sh) Instead of putting up a now.sh app I would have shown how to set up url-s in the host so anybody could follow and reproduce the example.
  • there is a way to load .env files when starting the app so it would not be necessart to put the import and .config() everywhere I want to use them. node --inspect -r dotenv/config -r ts-node/register -r tsconfig-paths/register src/main.ts If this is used in package.json then itā€™s all done automatically.
  • I personally prefer nodemon but itā€™s a personal preference
  • I would not use roles on ui because permissions should define what can be shown.
  • on the UI (angular) I had to implement routeguards, link visibility and directive for restriction. It was a bit tricky, would be happy to share how I did it.

The article is well written easy to follow and understand and itā€™s greate to have one. Thank you. Please take my ideas as a personal opinion for improvement.

Would be really happy to get involved in next steps for this topic.

1 Like

Hey there @puffancs!

Have you considered joining our Auth0 Guest Authors program?

Sounds like a perfect fit!

:pray: Feedback like this helps us all grow and become better. Thank you for taking the time to read the post and to leave this detailed comment.

Iā€™ve been working over the past weeks to improve the language of the tutorial and do some additional research. It started as a foundation of what identity in NestJS would be like.

Let me address your points one by one, please:

The client side code would be interesting to go through. How you implemented token refresh, audience handling etc.

Absolutely. This is on the pipeline :slight_smile: It would be a tutorial of using React with TypeScript.

I have not used passport but express-jwt and a middleware. I believe passport is an overshoot for this task and makes life a bit more complex then it should be. If a middleware is used with a custom jwt validation (which is done in the startegy anyway) to store the jwt on the req object and a single AuthGuard is created then it would remove a bit of magic from the flow and 1 guard would be enough instead of 2

I debated over this one haha! I went with Passport because NestJS provides that NestJS module out of the box and it seemed like a good idea to show how to integrate it. Definitely for the use case here, Passport is ā€œover-engineeringā€ as there are no sessions, etc.

I will be adding the option on a different chapter to use a simple middleware function instead. What do you think about it?

Using guards would be problematic with graphql in my opinion, because every ResolvePropery should be protected anywhere it is used. Because of this I have put the authorization logic in the service layer.

GraphQL was not on my radar, but as Konrad commented, if you like to write content and this is something youā€™d like to contribute to our blog, by all means, Iā€™d invite you to apply to the Guest Author program :slight_smile:

However, making the code more adaptive to different architectures is a solid goal to have. How did you go about putting the authorization logic in the service layer?

On the client side itā€™s ā€œtrickyā€ to know how to force auth0 to use jwt access_token-s (audience has to be specified)

Is this feedback about our documentation not being clear on this or the process not being intuitive?

It would be greate to know what is the best practice if I have multiple api-s? How do I handle it on the UI? (The ui should request the access token? Or the back-end?)

This would be a great topic for a general post without being connected to a particular backend technology, for sure.

The jwt parse is a bit simplisitic. The revoked token handling is not implemented I believe

Right at this time there is no refresh token. I am considered adding that as an extra chapter as long as guidance on how to implement short-lived tokens. Something similar to what @zacksinclair has done.

express-jwt is used in all the old docs and in the example code but itā€™s not really maintained

You are correct. Apologies for that. I have been conducting internal research and discussion to address this.

the client callback has a bit of black magic to it (dashboard.whatabyte.now.sh) Instead of putting up a now.sh app I would have shown how to set up url-s in the host so anybody could follow and reproduce the example.

The goal of the demo client was to quickly set up a working client without much set up other than creating an Auth0 application. Something like this would be possible down the road. I wanted to also try a "Deploy withā€¦ " button or something of that nature. What do you think of the length of the tutorial? haha I didnā€™t want to make things too long :grimacing:

there is a way to load .env files when starting the app ā€¦

Great tip! Thank you!

I personally prefer nodemon but itā€™s a personal preference

I like it too! :cowboy_hat_face:

I would not use roles on ui because permissions should define what can be shown.

Another debate that happened :slight_smile: Iā€™m taking this part in consideration as well.

on the UI (angular) I had to implement routeguards, link visibility and directive for restriction. It was a bit tricky, would be happy to share how I did it.

It would be interesting to see what youā€™ve done in Angular for sure. I think NestJS may be more popular with Angular developers because of the feature parity and similar architecture constructs. Thank you!

1 Like

Huge kudos for this great write-up Dan!

Hi Dan,
Thanks for your great article. Itā€™s really help me much when learning Nest and Auth0.
I found a tiny typo issue at section Getting User Roles when you explaining how to add roles to the access token:

const accessTokenClaims = context.idToken; // It should be context.accessToken

Iā€™m waiting your next chapters that guide us implement a client application that handle token, refresh token, validate the user roles/permissions if you will.

1 Like

@tammai Thank you for reading and for your feedback. Yes! I have that typo fixed in the update that Iā€™ll be publishing this week :pray: Sorry about that!

Everyone. The updates for these chapters are going to be reviewed this week and weā€™ll be pushing them as soon as possible. Thank you for your patience :pray: Weā€™ve been working hard on taking everyoneā€™s feedback and improving the tutorial.

1 Like

Thanks a lot for all the hard work on this Dan!

Hey Dan - checking to see if you could review my previous questions? Thanks in advance!

1 Like

They have been in my tasks queue for the past weeks. Sorry for the delayed, which was mainly caused by the holiday season and :beach_umbrella: :pray:

A few of them will be addressed in the update:

#2 There was a mistake in the language used and I went deep into getting this clarified. Weā€™ll also need to update some documentation.

#3 You are right. The tutorial focus is only on authorization, as such the artifact to use for that process is the access token. The waters became murky because we are introducing authentication indirectly through the demo client as to get a real, production access token. So we do ā€œAdd authenticationā€ to the tutorial flow but we donā€™t implement it. Passport was definitely not needed for this API but I wanted to use it since there is a NestJS module for it. I will be adding an alternative track where you can enforce authorization using a middleware function instead. The ID Token is designed to be consumed by the client application only for tasks like populating user profile data on the UI.

#5 The repo will be shared :slight_smile:

Pending for further research and a POC:

#1 I consulted with our Solution Engineers about the use case where permissions may change between calls and how to deal with them. I am working on a POC on the different approaches which they suggested and get them reviewed. We want to be sure we provide you solid guidance on this with security in mind. I can tell you that your approach is not too far apart from one of the proposed strategies. This one will take a bit more time.

#4 Also awaiting review of this answer to relay it to you :slight_smile:

You also were right about the PermissionGuard bug! I tested it myself :man_facepalming: The wires crossed lol It was meant to be:

    const hasPermission = () =>
      routePermissions.every(routePermission =>
        userPermissions.includes(routePermission),
      );

Thank you for bringing it up. That is also part of the update. We want to ensure that all the permissions required by the endpoint as present in the permissions claim of the access_token.

Zach, hereā€™s a preview of what the authz middleware would be like. Iā€™d love your feedback:

Your JWT middleware is basically identical to mine - I also implemented this w/o passport.

Iā€™m still mildly unclear on the official position on id_token and access_token with regard to authentication. ā€œThe ID Token is designed to be consumed by the client application only for tasks like populating user profile data on the UIā€ - this implies the token must be stored insecurely (ie be accessible to client side javascript) - and is therefore not useable for authenticating a request to the server. If that is the case, access_token would need to be used for authentication, but weā€™re back to this issue where access_token is supposed to be strictly for authorization.

My implementation is as such:

  • id_token, access_token, and refresh token are stored in HTTPS, http-only cookies (and thus unaccessible to client side JS)
  • id_token is used to authenticate user in middleware and attach JWT + user object to request for subsequent use in queries etc
  • I currently do not use access_token to check permissions and instead call the Auth0 API as described in previous comment. I could switch to access_token, but it brings up in token revocation issue, which has its own inefficiencies and downsides. I know its inefficient to call Auth0 API for every request; I simply cache the response for ~10 minutes, so its not such an issue. Basically, I authenticate user with id_token, then get their permissions from the API.
  • To display user data in the UI - I have a graphql get user query that decodes the id_token sent in the cookie and returns the relevant info for display in UI (since the cookie is http only, i need the server to do this). This allows best of both worlds; id_token is securely stored (thus useable for authentication) and still effectively used to display user info in UI.
  • I also have a series of decorators that allow authentication and authorization checks for both any given controller and/or any given graphql query/mutation, such that I can require login and/or specific permissions for absolutely anything returned to a client, either via standard REST or Gql

One point about your jwt middleware: youā€™re calling it AuthZ, but all its doing is validating a JWT - which is AuthN. I keep getting back to this distinction with regard to id_token and access_token and their respective uses for authN vs authZ.

Zach, to better assist you, could you please clarify what you mean by ā€œauthenticating a request to the serverā€?