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 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:
- The access token will never be tailored to an application, always to an API, so the
aud
claim wouldnāt match. - 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.
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:
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
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:
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!
You are welcome! I am glad that we could help 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
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!
@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? 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
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ā¦
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 )
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
- Would it be possible to check the roles/permissions against the
@Permissions
metadata within thevalidate
method of the firstJwtStrategy
AuthGuard
? - Why use
dotenv
instead of@nestjs/config
(NestJSās configuration package which utilisesdotenv
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.
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
Thanks for the post you should consider Connecting a NestJS application to a MongoDB or PostgreSQL store. For your next write up.
Thanks
Adan
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!
Howdy, Adan! Welcome to our Auth0 Community.
Itās on the pipeline Weāve been just super busy with a few other items. The updates should happen within this quarter for sure!
Echoing what Konad mentioned: aside from the usual storage of read/write data from a CRUD API, is there any particular use case you have in mind, specially in relation to Auth0 or identity management?
Thank you!
Thank you for the reply! For example right now I am working on a nest server that works not with mongoose but with mongoDB driver. My biggest question is how to implement Auth0 with the database.
What is the best practice on how to link an authenticated and authorized user to the users data.
By the way I think I have seen a blog post regarding this, Iād like to see how NestJS can be used to link users data from Auth0 to whatever database the developer is using.
Thank you!