ID Tokens vs Access Tokens
Fleeting- External reference: https://github.com/IdentityServer/IdentityServer3/issues/2015
- External reference: https://developers.google.com/identity/openid-connect/openid-connect
- External reference: https://tcm-sec.com/id-tokens-vs-access-tokens-whats-the-difference/
- External reference: https://darutk.medium.com/api-protection-by-id-token-3123481e96f2
- External reference: https://community.auth0.com/t/why-cant-an-id-token-be-used-to-authenticate-an-api/62430
- External reference: https://security.stackexchange.com/questions/256061/oauth2-using-id-token-for-authentication-to-a-backend-service
- External reference: https://discuss.kubernetes.io/t/why-use-id-tokens-instead-of-access-tokens-for-authorization/27105
- External reference: https://auth0.com/blog/id-token-access-token-what-is-the-difference/
- External reference: https://auth0.com/blog/identity-unlocked-explained-episode-1/
- External reference: https://oauth.net/id-tokens-vs-access-tokens/
OIDC comes with the notion of id token. This is a JWT formatted token, and because a lot of authorization servers format the access tokens also using JWT, people tend to confuse both1, 2, 3, 4.
This leads to people using id tokens as access token interchangeably and other people shouting that this is something “YOU MUST NEVER DO!” without much argumentation.
Let’s try to bring some nuance in here.
usage made by some well known players
First, let’s note that some well known tools use the id token to get access to first party data5, 6. So we might be puzzled that even big actors don’t follow those recommendations.
As stated in the documentation of google, there are situations where authentication is enough. For those, using an id token is enough7. They appreciate its ease of use but they emphasize of course the need for validating it8.
This is by no mean an argument in favor of using id token in place of access token, but it is a hint that the question might be broader than “using id token or access token”. It might rather be more like this: in a particular situation, can I send an id token? can I send an access token?
And, most of all, it asks whether we are falling into the black or white fallacy and suppose being able to implement a security model with access token would mean that we cannot implement it with an id token.
deontological point of view vs pragmatical point of view
Often, we see that “YOU SHALL NOT SEND AN ID TOKEN AS AUTHORIZATION TO THE API”1 because it is not meant for that. But alone, this does not weight much on the decision. If you face a case where it makes sense to do that, you’d better understand why not doing that and not just listen to method priests.
When looking for articles about access token vs id tokens, it seems to me that there are a lot of them trying to prevent the so-called “misuses” of id-token but with only superficial understanding of the threats model. They look like they beg the question. They start with the belief that “you shall not send an id token” and try to rationalize this afterwards.
This leads to arguments that look very poor and often not appropriate.
For instance, some forgot about the authorized party claim9 and get into the slippery slope of thinking other kinds of misuses10 that are not relevant with the question at hand. For instance, not explicitly asking for the user consent totally makes sense in the first-party scenario.
It is even forgotten that permissions can take a lot of forms and that access tokens are only one way to grant them11.
In the end, those argument look like this: “use only access token”, because “this is not meant to be used like that” or “it can lead to unauthorized access to resources”12.
Also, some security risks are valid both id tokens and access token, making them irrelevant in the decision of using one or the other13.
audiences: client vs resource server
The JWT payload of the ID Token is quite explicit, this is meant to be consumed by the client (called the relying party in OIDC) while the access token is meant to reach the resource server14, 1. When the later is self-encoded, its audience claim makes this even more explicit.
In some cases, where you have a SPA that consumes some resources from your server, the only think you need is a proof of authentication to get access to the resources of the user. When using an Identity Provider, you define an application and get a meaningless string as client id like “3faa352f-a50e-46a0-a286-b6a001f0fed5.apps.googleusercontent.com”. Deciding that the server and the frontend ARE the client and that therefore “3faa352f-a50e-46a0-a286-b6a001f0fed5.apps.googleusercontent.com” refers to both makes sense. Therefore, the argument about the audience appears to only become a semantic debate more than anything else.
usage: authentication vs authorization
When receiving an id token, you have a proof that a user authenticated, but that says nothing about whether or not this user has access to what15. On the contrary, an access token says nothing about the user, but indicates a right to access some resources16. Both may be related, as sometimes, some claims about the user are needed to perform authorization decisions.
Therefore, both authentication and authorization are needed in an authorization flow17.
Actually, it seems like authentication and authorization overlap a bit in several real life use cases. To me, this is because is those use cases, the client and the resource server come from the same organisation, making it a first party client. In that case, a proof of authentication may be the only needed information to grant access to resources18, 19. In fact, even in the third party scenario, as long as the resource server knows which client gets access to what resources, the id token, conveying the information about a user AND an authorized third party, is enough to discriminate what resources to grant access to and what to deny access.
Authorization, as part of oauth2, comes with some extra RFCs to allow more secure means of communication, called sender constraints. This makes sense in the third-party case, because the access token will be shared with some systems out of the scope of the system dealing with the authorization.
An article of auth0 explains that even though in the first-party scenario, sending an id token might be enough20, the sender constraints make using an access token better suited, security wise21.
They insist that this decision of sending an id token or an access token only makes sense in the first-party scenario. In the third party scenario, there is no question that only an access token makes sense22. This can be nuanced as well, because as long as your authorization server knows the client and checks that the received id token was issued explicitly to this client (using the azp Claim), then there is no reason to refuse this client access to the resources that you know it should get access to.
Unfortunately, their argument is based on a slippery slope, where they assume that you make no verification at all about the audience23, 24, while an ID Token issued only for a third party client for which we know the kind of authorization we need, we can check the azp Claim and the audience claim.
This asks the question whether the other arguments of the article were also tainted to persuade not to send an id token instead of looking at the situation neutrally.
architecture arguments
One critic against using the ID token is that they are passed though the bearer token transport that was intended in oauth2, for access tokens25. To my mind, this is partially valid, as using stuff like an access token might lead to difficulties to explain the model afterwards, but by no means using the “Bearer " keyword should provide an security issues, if the content is appropriately processed.
One might argue as well (yet I did not actually read that) that a well organised architecture will give less room for bugs involving security. If the authorization server is responsible for providing authorization and the resource server is simply reading the authorization claims without making more clever thinking, like filtering based on the authorized third parties and user identity, we may reduce the possibility of getting through its security. I’m not totally convinced, as you have to configure the resource server anyway to map some claims to some resources, so using the “azp” and “sub” or using “foo”, “bar” look identical to me, security wise.
final note, is that only a semantic debate?
I could read arguments stating that if the resource server needs some information about the user to tackle authorisation, one can provide the content of the id token, but should make it part of the payload of the access token26. They insist on the need to follow the standards to avoid falling into subtle traps that experts did not analyse, because we got into uncharted territory27.
Therefore, it strikes me that − ceteris paribus − deciding whether a particular program should be called a resource server or part of an authorization server (getting an identity from an Identity Provider for instance) will change the perspective of whether an id token or an access token is suited in a particular situation28.
That would mean that the debate could be not about using such or such artifact, but being very clear about the function of the parts of one’s architecture.
Therefore, I believe that most of those discussions about using an id token or an access token miss the point, and are question substitution for “what are you trying to achieve and how are you going to use oauth2 and oidc to achieve it?”
Notes linking here
- As an openid provider (blog)
- keycloak provide many user related information in the access token by default.
- making sense of github OIDC flow
- OAuth 2.0
- OAuth Sketch Notes Q&A - PKCE, Scopes, Security, Passwordless
- OAuth – the good Parts - Anders Abel
- sender constraints
- using id token as access token?
Permalink
-
- ID tokens are meant to be read by the OAuth client. Access tokens are meant to be read by the resource server.
- ID tokens are JWTs. Access tokens can be JWTs but may also be a random string.
- ID tokens should never be sent to an API. Access tokens should never be read by the client
-
our API endpoint receives a request with a token, then we first validate the token and extract user_id (authentication part) and second, we do some authorization logic (e.g. check that payment.owner_id == token.user_id), so actual authorization happens outside of OpenId/OAuth2, but we use user_id taken out of a token. And in this case either token works
-
people abuse ID tokens and access tokens all the time, in part because, at least until very recently, there was no standard for JWT formatted access tokens
— https://community.auth0.com/t/why-cant-an-id-token-be-used-to-authenticate-an-api/62430
-
Identity wizards will likely balk at it but I don’t think there any significant harm, especially given this is all “in house”
— https://community.auth0.com/t/why-cant-an-id-token-be-used-to-authenticate-an-api/62430
-
how to reconcile the documentation from 0auth and Okta explicitly stating that ID Tokens should not be used as credentials to a Resource Server with Kubernetes explicitly providing a flow for doing so.
— https://discuss.kubernetes.io/t/why-use-id-tokens-instead-of-access-tokens-for-authorization/27105
-
by the spec and the intent of the oidc spec writers okta is correct But that’s just not how it’s been used across APIs (not just kube) Defacto standards vs standards written
— https://discuss.kubernetes.io/t/why-use-id-tokens-instead-of-access-tokens-for-authorization/27105
-
#+BEGIN_QUOTE following are common situations where you might send ID tokens to your server: ↩︎
-
One thing that makes ID tokens useful is that fact that you can pass them around different components of your app. These components can use an ID token as a lightweight authentication mechanism authenticating the app and the user. But before you can use the information in the ID token or rely on it as an assertion that the user has authenticated, you must validate it
— https://developers.google.com/identity/openid-connect/openid-connect
-
client that has no relationship with the resource server can access APIs of the resource server using an ID token that the client has legitimately obtained in an utterly irrelevant context.
— https://darutk.medium.com/api-protection-by-id-token-3123481e96f2
-
user has granted a permission for the client to get an ID token, but she didn’t imagine that the permission would enable the client to call APIs of the irrelevant resource server.
— https://darutk.medium.com/api-protection-by-id-token-3123481e96f2
-
possible options for API protection are just “all allowed” or “all denied”.
— https://darutk.medium.com/api-protection-by-id-token-3123481e96f2
-
Misusing ID tokens this way can lead to vulnerabilities, such as unauthorized access to sensitive resources.
— https://tcm-sec.com/id-tokens-vs-access-tokens-whats-the-difference/
-
if an ID token is intercepted and the system doesn’t validate it properly, attackers can impersonate users.
— https://tcm-sec.com/id-tokens-vs-access-tokens-whats-the-difference/
-
they are two different tokens and do totally different things.
and Importantly, we say that they have different audiences. […] the audience of an access token is the resource server, whereas the audience of the id token is the client application.
—
-
ID tokens are conceptually analogous to ID cards, in that they contain a set of claims about the user, like name and email. This is different from an access token, which does not include any identifiable information and instead exists to authorize access to resource servers with limited scope
— https://goteleport.com/blog/how-oidc-authentication-works/
-
Access Tokens do not include any user information
— https://goteleport.com/blog/how-oidc-authentication-works/
-
Possession of the access token is not proof of authentication because access tokens can be acquired in multiple ways. Consider refresh tokens, which a client can exchange for another access token when the original is invalidated. This process occurs automatically between machines and does not involve the human user in any way, so the access token is not a proxy for authentication. ID tokens can only be obtained when the human user explicitly gives a client access to whatever information it requires. In other words, the user must go through the process of “Sign[ing] in with Google,” which only the account holder is capable of doing,assuming credentials have not been compromised
— https://goteleport.com/blog/how-oidc-authentication-works/
-
If you accurately described your issue in your question, you want authentication, so you should use ID Tokens.
-
What you describe is authentication, then authorization, in 2 steps. In your case, authorization is done with business logic, using the (previously authenticated) identity. Access Tokens are not suited for your needs, you should use only ID Tokens
-
In a first-party scenario, i.e. in a scenario where the client and the API are both controlled by you, you may decide that your ID token is good to make authorization decisions: maybe all you need to know is the user identity
— https://auth0.com/blog/id-token-access-token-what-is-the-difference/
-
there is no mechanism that ties the ID token to the client-API channel. If an attacker manages to steal your ID token, they can use it to call your API like a legitimate client.
[…]
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. This guarantees that even if an attacker steals an access token, they can’t use it to access your API since the token is bound to the client that originally requested it
— https://auth0.com/blog/id-token-access-token-what-is-the-difference/
-
In a delegated authorization scenario where a third-party client wants to call your API, you must not use an ID token to call the API
— https://auth0.com/blog/id-token-access-token-what-is-the-difference/
-
If your API accepts an ID token as an authorization token, to begin with, you are ignoring the intended recipient stated by the audience claim
— https://auth0.com/blog/id-token-access-token-what-is-the-difference/
-
API shouldn’t accept a token that is not meant for it. If it does, its security is at risk. In fact, if your API doesn’t care if a token is meant for it, an ID token stolen from any client application can be used to access your API
— https://auth0.com/blog/id-token-access-token-what-is-the-difference/
-
Some developers use ID tokens as access tokens, which is incorrect
— https://tcm-sec.com/id-tokens-vs-access-tokens-whats-the-difference/
-
what if you want to flow the Identity of the user to the backed, and the API is part of your trusted ecosystem, then you, either:
a. Add User Id, Name, Email, etc. claims to the access token(s)?
or
b. Pass the IdToken (as a bearer) through these layers?
[…]
access token is for the back end and it would contain the claims you need/want (or you could use the access token to call the user profile endpoint to look them up).
[…]
Access tokens are for APIs - feel free to send the id_token (or its contents) along - but treat it as payload.
[…]
If the OAUth2 and OIDC specs were re-designed today, maybe it’ll all be different and you could do this. But there’s history to how they were developed, and so it’s best if we stick to using them how they were designed so as we don’t introduce subtle security vulnerabilities into our apps
— https://github.com/IdentityServer/IdentityServer3/issues/2015
-
yes they are doing it wrong. All specs and threat models are clear on this. id_token is for the client, access token for the resource server. Now you might come up with a very specific scenario where using the tokens differently might be OK - but you are basically in uncharted territory (at least from a spec point of view)
— https://github.com/IdentityServer/IdentityServer3/issues/2015
-
there is a case where sending the ID token to a server, which I would not classify as a resource server, seems to make sense. This is when the ID token is used to authenticate and identify the user to another system. In this case the server, after verifying the token, would perform some action such as create an authenticated session or issue its own access token to provide API access against its resource server.
— https://github.com/IdentityServer/IdentityServer3/issues/2015