Thank you for this very nice article, very interesting and mind opening.
After reading the following:
For the access token, on the other hand, there is a set of techniques, collectively known as sender constraint, that allow you to bind an access token to a specific sender.
I was thinking, can I not use the ID token with the sender constraint techniques such as DPoP? What is preventing me to use the ID token instead of the access token togheter with DPoP?
Bear with me guys, if this a stupid question with a simple answer I cannot see…
Regards,
Luca
Hey @finrod2002,
Thank you for appreciating this article
What is preventing me to use the ID token instead of the access token togheter with DPoP?
Nothing, apart from support from the identity provider
I mean, DPoP has been defined for access tokens (see this article for a DPoP overview).
Access tokens are meant to be used by a client to access a resource protected by a server. They are designed to be transferred from the client to the server.
The risk associated with a bearer token is that an attacker can intercept it and use it instead of the authorized client. DPoP overcomes this potential risk.
An ID token is not intended to be transferred to another entity. The recipient is the client and must remain with the client. So, there should be no risk associated with transferring it to the network because it should not be transferred.
Of course, all of this is true if you want to follow standards (OIDC and OAuth2, in this case).
Everyone is free to interpret the standards creatively, which can open up new ways of doing things as well as unexpected problems.
I mean, a kitchen knife is meant to cut bread, but no one stops you from trying to cut wood with it
Thank you for this clear response. This helps me a lot.
Where I’m still struggling is around the definition of “client”. Let me explain it with the project I’m working on as example. I’m implementing a SCIM (System for Cross-domain Identity Management) endpoint to work with an external identity provider. We have a number of clients that can be used to login (desktop, web based, mobile). From the external identity provider we get an ID token (and access token of course, but I would like to leave that out of scope). Also we get a clientId which corrisponds to the audience claim present in the ID token. So my guess would be that from the point of view of the external identity provider all our applications (APIs, desktop, web GUIs, mobile) are one client.
To explain further, I would like to avoid to having to validate and inspect the ID token in every frontend application, but would prefer to pass it via an API call to a server which does that for the frontend clients.
Is this a valid use case? Can this server than be seen as part of the authorized client? I mean the server is under my control, so I can check the audience claim present in the ID token. Or will that be seen as well as a case that the ID token is “transferred somewhere else”, in this case from the frontend application to the backend?
Edit: Clarified last sentence
I’m not sure it’s a good idea to have a single client ID for all different types of clients (SPA, Web, mobile, desktop). You may have the need to configure things differently for each one, such as token expiration time, callback URLs, etc.
Maybe what you are looking for is a sort of Backend For Frontend (BFF). BFF is a popular design pattern for SPAs that basically uses a regular web application as a proxy between the SPA and the Identity provider. In short, the SPA NEVER receives the tokens. They are all managed by the web application, which exposes some endpoints to provide user profile info and forwards calls to APIs. BTW, a blog post on this topic is in my pipeline.
In your case, you might consider extending the pattern to the other types of applications as well. Does this make sense?
I agree with you that ideally we would like to have different clients. The procedure to publish a client at the external provider (Microsoft) is quite cumbersome though, so I prefer to have one for practial reasons, at least for now.
I like the idea of the SPA, mobile and desktop clients never receiving the ID token and I think it is doable for us. Not really to implement a backend for every frontend (as it would basically me alone which has to develop, deploy and maintain different backends), but I think it is good to keep the basics idea in the back of my mind and structure the code in a similar way even if in the same backend service.
Regarding the ID token never being received by the SPA I see two possibilities with regards to the authorization code flow. Please correct me if my understanding is completely wrong.
-
Configure the backend URI as callback. That way the backend receives the authorization code directly from the external provider.
-
Let the SPA get the authorization token and send it to the backend. The backend will exchange the authorization token for ID and access token.
First one, would be nicer and probably saves some network calls, I guess. But the changes required would have quite a big impact at this point for us. On the other hand the second possibility requires only small changes and would be doable. Is that one acceptable in your opinion?
Hey @finrod2002,
Option 1 is the best approach. This is how the BFF pattern works. All interactions with the authorization server are handled by the backend.
Option 2 simply does not work, unless you want to hack the specific OAuth2 flow for SPAs.
Basically, the SPA uses the PKCE extension to protect the authorization code from getting intercepted. This would prevent the backend from using the authorization code on behalf of the SPA.