Developing a Secure API with NestJS

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”?

Also, Zach, could you please share what is the full architecture of your application, please? That will help us understand better what your identity management goals are :slight_smile: Specially if you could please point the starting points for authentication and how things flow from there.

Hey there, @zacksinclair.

Well, actually, just because we say that the ID Token is designed to be consumed by the client application, it doesn’t really mean that it must be securely store. ID Tokens are JWTs, hence they are temper-proof. That is, you can send it in plain text and still be sure that the other side will be able to validate that the token is valid. This occurs by leveraging private and public key pairs.

In relation to access tokens, Auth0 does use JWTs, so it might look like they could be used in the very same way. However, this is not the case. Although it is technically true that you can validate the signature of an access token issued by Auth0 (from the client application, I mean), there are a couple of problems on doing so:

  1. The access token will never be tailored to an application, always to an API, so the aud claim wouldn’t match.
  2. OAuth 2.0 doesn’t impose a format for access tokens. So, Auth0 could simply stop using JWTs and move to whatever format it would like or no format at all (random string).

Having that in mind, you should always use access tokens as opaque strings.

Apart from that, just because we do issue access tokens as JWTs, it doesn’t really mean that the user is authenticated. In fact, if you do take advantage of refresh tokens to fetch new access tokens, the user might not be authenticated for a long time… All an access token states is that some time ago the user granted the application some authorization to consume the API on its behalf.

In summary access tokens are artifacts that carry information about delegated authorization.

To be honest, I haven’t read the full thread. Just the message you typed 2 hours ago, so, if I missed something, sorry.

If you have questions, let me know.

@bruno.krebs I’m clear on the majority of your points with regard to JWTs, opaque strings, storage, etc. Even with the last couple comments from you guys, I’m still a little unclear on authN (id_token) vs authZ (access_token).

I see it like this:

  • Authentication - id_token - is the user logged in and who are they
  • Authorization - access_token or Auth0 permissions API - does that user have permission for X / have they granted you access to take X action on an API

My flow is:
Delegate login (authentication) to Auth0 via OAuth + OpenID flow, and my server gets back an id_token, access_token, and refresh_token. I then store these tokens in the user’s cookies.

That user makes another request to my server. I authenticate them by validating their id_token. This allows them access to login-required pages / endpoints / graphql queries, and I associate the request with their local user profile etc.

This is the point I’ve been trying to make with regard to securely storing id_token: since I use it for authentication, it must be stored securely. I’m not worried about tampering (I still validate the tokens signatures) - I’m worried about who has the token, because anyone who has the token can authenticate themselves as that user. Therefore must be stored securely.

In other words, if one uses id_token w/o storing securely, how do you authenticate the user securely? Docs and the oauth spec specifically state not to use access_token for it.

Your docs here state to never use access token for authentication. Thus me using id_token for it. Thus storing it securely, and being confused why so many comments say to allow client side JS to read it for displaying user info - this is a security hole. It needs to be such that the token is passed to the server via cookie, server decodes it and sends back the user profile info.

Back to flow: if the user tries to access a page requiring specific permissions, well now I need to check their permissions in addition to authenticating who they are. I have two options: 1. check the permissions in access_token or 2. call Auth0 get permissions endpoint. As I’ve described I call the endpoint b/c it is easier than dealing with revoking refresh_tokens - though at some point I’ll probably need to support revocation anyways.

1 Like

Zach, let me address first the concepts the usage of Access Tokens vs ID Tokens. The following is based on one of the videos created by Vittorio in our “Learn Identity” series: Calling an API

On the “Calling an API” video, on 33:27, you can hear Vittorio recap on “Access tokens vs ID tokens”.

Here are the highlights of his recap:

Access Token:

  • An access token is meant to grant a client access to a resource (delegated authorization).

  • Per the OAuth 2.0 spec, there is no mandated format for the Access Token but most people, Auth0 included, use JWTs.

  • Clients can request and use access tokens but not inspect them.

ID Token:

  • ID Tokens are meant to signal that a successful authentication occurred. These tokens are designed to support sign-in and authentication information.

  • Per the OIDC spec, the ID token is formatted as a JWT.

  • An ID Token should never be sent to any entity other than the client itself.

Vittorio points out that there is a lot of controversy about this. The ID token is really designed for sign in, and so, usually, once you receive it, that means that you are the client because the receiver and recipient of the token are the same entity. So, normally the ID token shouldn’t travel farther.

Vittorio also mentioned that there are situations in which, from a security perspective, it’s potentially okay to use the ID token for making a call to an API. Those situations are largely limited to a case in which the client, as in the component that calls APIs, and the API itself happen to be the same logical application.

In this case, you don’t necessarily need delegation information because you are just a first-party app. From a security perspective, there are a number of flows in which having acquired the ID token or the access token makes no difference, or makes no discernible difference. This may be your case.

An ID token could be seen as an access token with specialized semantics. So, as long as the audience that you have in the ID token corresponds to the identifier of your app you could do that; that is, it’s all within the logical boundaries of an application. Just never send the ID token to a third-party API. This should always be targeted to your own application, the same application that requested it.

However, Vittorio warns that it’s very controversial because a lot of people will simply argue that the specification says you should use an access token, but the specification doesn’t really cover the first-party scenario. It’s really designed to describe the third-party scenario, but you end up arguing semantics.

So, unless there is a huge advantage in terms of convenience under the first-party app model, it’s recommended to use the access token.

The scope of this blog post is to provide guidance for a web application that uses a client-server architecture where the client is in charge of authenticating the user and obtaining the ID token and the access token. The client then sends the access token to the server to make requests on behalf of the user and the server uses the access token to determine the access level the user has. Here, we recommend using the access token for delegated authorization and using the ID token as the result of authentication in an OIDC context and to encapsulate user information that is consumed by the client application.

Since you are following a different architecture that warrants a different level of discussion beyond the scope of this tutorial, I recommend for you to start a separate thread under the General category, please:

This will help us provide you with better visibility and advice on this particular topic and we could also understand better your architecture and scenario with the things that are unique to your application.

In that thread, I’ll recommend that you share with us a more detailed description of what you currently have: for example, what is the client, what is the server, perhaps with a flow diagram.

This detailed document on Authorization may also help further distinguish the boundaries of authorization in an application stack:

2 Likes

Howdy, everyone! I have updated the blog post to integrate multiple feedback from this thread and also address some confusing language around authentication and authorization. I hope it’s clearer to understand now :slight_smile:

Rotating the access token:

This is a detail implementation of the client that is opaque to the API and could vary. Under the hood, the WAB Dashboard is using RxJS and the Auth0 SPA SDK to power an authentication service. Anytime you refresh the Dashboard, a new instance of an Auth0 client is created with a new and updated access token.

The repo is here:

GitHub - auth0-blog/wab-menu-api-nestjs: Learn how to use NestJS, a Node.js framework powered by TypeScript, to build a secure API :+1:

Dan - first off, I really appreciate how thorough you’ve been in helping review and discuss this; thank you.

I think the ultimate source of confusion is this distinction between the type of app - first party or third, who the client is, etc. My scenario is as you’ve described - my client is my own website and app and my server directly corresponds. I’m only authenticating and authorizing within the bounds of my own app; no calling out to third party APIs etc.

“makes no discernible difference” is effectively true for my app, with the only distinction being what data the token carries - id_token doesn’t have permissions but does have more profile info.

I guess another point of (previous) mild confusion was around what exactly I’m authorizing; if a given endpoint does not require a permission, but only that you’re logged in, then validating the access_token is enough; your authorization is that you have a valid access_token (and I just don’t need to check for a permission). And in this model, I would stop using id_token as authentication.

I’m going to skip starting another thread for now as I think I’m clear enough after all of your help and I’m comfortable with the approach my app is taking. Thanks again!

1 Like

You are welcome! I am glad that we could help :pray: Let us know if you need anything else. I am still going to be working on a blog post about using the Manage API to get the permissions and short-lived tokens :+1:

What’s your email? I’ll send some helpful bits for your other post.

Zack, thank you for your offer. You can send me a direct message through this site. Click on my avatar, then on my profile page, you can click on the “Message” button. Thank you!

1 Like

@zacksinclair, @puffancs, @mob I’d love to have your insight and feedback on this, please:

I want to integrate the authorization process using Middleware as an alternative to Passport, or vice versa. However, I don’t want to make it too complex or confusing for readers to follow along.

I am thinking on doing the following:

  • Introduce Authorization using Middleware first.
  • Introduce role-based access control using the middleware implementation.
  • Provide Passport chapter as an optional, nice-to-know chapter.

What do you think? :thinking: The alternatives are to provide Middleware as the optional content or to provide readers with two paths to take: Passport or Middleware (but I think this ends up creating a messy tutorial).

Thank you for your time and insight on this :pray:

I think your approach makes sense.

Middleware without passport is certainly a more straight forward introduction. Its not a lot of code as middleware anyways and passport abstracts out a lot of the concepts that are healthy to understand when you’re implementing this kind of stuff.

Re. RBAC: I generally prefer straight up lists of permissions and use roles to assign permissions to users (as opposed to something like requireRole(‘admin’))

Passport chapter as nice to know is appealing; a large app with various ways of auth/auth will likely end up going the passport route. It becomes efficient when you need to support 2+ ways of auth’ing (e.g. API access w/tokens, users on website, users in app).

I think the two paths approach is where this should eventually go, but have them be distinct from each other. Intro post says there are two ways to do this stuff, straight up middleware or passport middleware. Then two paths showing it. A lot more work to be sure…

1 Like

If I’m understanding correctly, the Middleware approach would be the more NestJS-low-level-native way of doing things, and Passport a more high-level way?

If this is the case, I’m all for a more low-level focused tutorial first, as it would contain and explain the underlying concepts, making the introduction and use of Passport (or any other library that abstracts stuff) probably easier to understand afterward.

Plus, as you previously said, depending on the app’s scale, using Passport might be a bit overkill, so that would provide readers a very logical and progessive approach: first learn the basics, then, if you need, see how to use a more advanced solution (provided that Passport is more advanced than Middlewares, which I wouldn’t know :smiley:)

Thought the article was great! When I first attempted at setting up Auth0 with NestJS I couldn’t find anything, so I ended up having to work it out myself (also at the time the pull request to integrate jwks-rsa with passport-jwt was still in the works), and an article like this would have been really helpful. I had a couple of questions:

Questions

  1. Would it be possible to check the roles/permissions against the @Permissions metadata within the validate method of the first JwtStrategy AuthGuard?
  2. Why use dotenv instead of @nestjs/config (NestJS’s configuration package which utilises dotenv under the hood).

Anyway, thanks for the great tutorial. I’m currently using some ideas from it to add to my nestjs-auth0 example/starter repo.

2 Likes

Hey there @jajaperson!

I’m sure Dan will cover that once he’s online!

These are interesting propositions. I am working on an update! This tutorial got more popular than I expect tbh! Love to see people using NestJS, so this makes me happy :cat:

Thanks for the post you should consider Connecting a NestJS application to a MongoDB or PostgreSQL store. For your next write up.

Thanks

Adan

1 Like

Hey there @panda2k17!

Can you tell us more about your usecase? So we can have more kind of justification for creating such content? Thank you!