No expectation of a reply (though maybe I have gotten some things here horribly wrong ). I just wanted to document the thoughts arising from my experiences above for anyone else reading thisâŠ
tl;dr - Should User Details Go Into The ID Token, Access Token Or Userinfo Endpoint?
- The details here should hopefully answer the final point raised in this other Auth0 community forum thread - why is there a need to even use the userInfo endpoint
- For most developers using access tokens to guard a REST API powering a SPA, you will be expecting an âaudâ (audience) field. This is because your API will be likely configured in Auth0 and you will have specified an audience there. Thus, it appears to make no sense to not supply the âaudâ field when calling the API. This means that you will be using JWTs and not opaque access tokens.
- You will have a difficult choice - put any meaningful info about the user into the access token (as custom claims) OR surface this info via the /userinfo endpoint butâŠ
- In Auth0, any custom claims that go into the /userinfo endpoint are automatically surfaced in the ID Token and this cannot be prevented. This does not appear to be an unusual practice - Microsoft Azure Active Directory seems to follow a similar approach. But it does mean that anything you surface in the /userinfo endpoint will be exposed to the front end in the ID token.
- This may impact the difficult choice I noted a moment ago. The developer may decide that they might as well put the extra user info into the access token (and consider encrypting those details if they are indeed sensitive). Because if those details go into the /userinfo endpoint, instead of the access token, they will be exposed in the ID token anyway.
- Alternatively, the developer may decide to keep these details out of the access token and the /userinfo endpoint (and thus the ID token as well). Instead, they may decide to use a proprietary API such as the Auth0 User Management API - however, such a choice means that every API secured in this way needs to make additional calls to the proprietary API and store yet more credentials to do so (more that if they could just have used the /userinfo endpoint - as that merely requires the access token).
- In summary, this means that if you want to keep user details private to your API and hidden from the front end, you need to (1) encrypt those details and put them in the access token or (2) figure out a non-OIDC (non-Open ID Connect) standard way of getting that information.
Long Version - My Best Guess At Why The Userinfo Endpoint Has Become Less Useful In Contemporary OIDC Architectures?
To give background to my above conclusions, I will outline my broad (possibly misguided) take on why modern OIDC implementations have evolved in this wayâŠ
-
I think that a long time ago (in the OIDC community generally) there was more focus on the /userinfo endpoint as being useful.
-
Since then, OIDC solution vendors like Microsoft have come to dislike the /userinfo endpoint because of the extra remote calls and the extra complexity. As this Microsoft Azure Active Directory article says:
Because you can get an ID token at the same time you get a token to call the UserInfo endpoint, we suggest that you use that ID token to get information about the user instead of calling the UserInfo endpoint. Using the ID token will eliminate one to two network requests from your application launch, reducing latency in your application.
-
At the same time, many people still like to claim that putting even basic user details - like the userâs full name - into an access token is a security risk. Instead, use the /userinfo endpoint. There are an endless amount of blog posts dedicated to this for example. (Sidenote: The difficult part for the developer responsible for implementing an OIDC-based solution is deciding whether this is security theatre or whether it represents a genuine risk). But, it would seem that this userinfo mechanism for covertly getting such user details to the server is no longer designed as mechanism to covertly get such user details to the server - because nobody really cares about using the /userinfo endpoint anyway when implementing a typical REST API to power a Single Page Application (SPA). Rather, the broad groupthink across folks using OIDC is that this is in fact security theatre. If you want details in the /userinfo endpoint then you are going to âleakâ those details in the ID token. As noted above, Microsoftâs implementation already concedes that. Similarly, Auth0 couples the information in the /userinfo endpoint to the information in the ID Token (which does not seem to be an unreasonable approach given the groupthink I have just noted).
-
Therefore, it seems to me, like any recommendation that says there is merit to âhidingâ claims from the access token is misguided, especially for basic details, like a userâs name. If you really want to hide these details you need to
(1) encrypt them OR
(2) establish an out-of-band backchannel to your OIDC provider (eg. the user endpoints of Auth0âs Management API ) or another user API that developer would implement themselves (which sounds like a lot of effort and may appear to negate the point of leveraging a service like Auth0 in the first place). -
The only other possible counter-argument to my last point, that I can think of, is that ID Tokens typically expire very quickly (eg. after 5 mins), whereas an access token can last for (for example) 2 hours. So, the ID token should provide a smaller attack surface than access tokens. That may suggest that there is some merit in putting basic user details into the ID token/userinfo endpoint but not into the access token. However, from a security perspective, it seems hard to justify - the fact that you consider details sensitive enough to omit from the access token would suggest to me that those details are also too sensitive to expose to the Javascript in an SPA application. Arguments could be made that access tokens may pass through more proxy servers, etc but that seems like a weak argument (as https is likely being used and access tokens would be in the request headers so they wouldnât typically be logged). I would be interested to hear other opinions on this from the community.
All in all, it seems reasonable that a developer would choose to put basic user details into an access token. Maybe I am underestimating the value of the arguably more ephemeral nature of the ID token (though it doesnât seem very ephemeral to me - as the ID token is commonly sent as an id_token_hint to help fully logout the user in some OIDC implementations). Using a /userinfo endpoint or proprietary user APIs to get basic user details adds complexity (especially the latter as it requires more credentials). For example, if an organisation has 10 APIs, that means that 10 separate codebase locations will need to call a userinfo endpoint (or proprietary user API) and likely implement a caching strategy for responses. This seems like a lot of work that can be mitigated by putting the basic user details in the access token - if an organisation is okay with the resulting tradeoff.