6

Many resources on the internet state that you should use Access Token and not Id Token to authenticate to an API, but do not provide explicit reasons why. Are there any real drawbacks to use an Id Token for authentication in a case when we control both parts - SPA web frontend and Web API. We can control lifetimes of both tokens (Id and Access) the same way and they can be refreshed using the same Refresh Token, so there does not seem to be any real benefit in using Access token over Id Token.

The reason we don't want to follow the standard path of using Acccess Tokens for authentication is that the provider we are using (AWS Cognito) does not allow us to add additional claims to the Access Token (e.g. user email), but allows adding claims to Id Tokens. Therefore we would need to implement some workarounds and Id Tokens just seem to be a much simpler approach.

4 Answers 4

4
+50

Access Tokens are not meant to authenticate an user (or application), but to authorize a specific access for short amount of time (minutes to hours). Source: RFC 6819. That's why additional claims should not be added to an Access Tokens, instead, another token should be issued when needed.

ID Tokens are not part of OAuth, but part of OpenID, a kind of extension to OAuth. They are meant to identify and authenticate an user (or application). Source: OpenID Specification. Once authenticated, the authorization to do or access something can be performed using Access Tokens or another mechanism. If you already have such an authorization mechanism in place, using Access Tokens would be redundant.

So, to answer your question directly, Access Tokens are meant to authorize an access to a resource, and ID Tokens are meant to authenticate a user. They have different purposes. If you accurately described your issue in your question, you want authentication, so you should use ID Tokens.

6
  • In our case, Authentication and Authorization are tied together - 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.
    – eddyP23
    Commented Oct 20, 2021 at 14:57
  • 1
    @eddyP23 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.
    – A. Hersean
    Commented Oct 20, 2021 at 15:01
  • Greatly appreciated, could you please share then how authorization looks like without business logic but with an access token. I am confused here, having an access token, you still need to do some logic that checks whether the action is allowed with a given access token
    – eddyP23
    Commented Oct 20, 2021 at 15:07
  • @eddyP23 The provider of the access token would do some authorization checks, like "does this user have read access to the details of this transaction?", then the token provider would write those answers in the token. The endpoint would verify the token, then check the access rights, without needing to check or to know the identity of the client. Whether this way of doing is best depends on the architecture of the software.
    – A. Hersean
    Commented Oct 20, 2021 at 15:17
  • What if the identity represented with ID Token is not the owner of the resource? For example you have an Support Team employee who by default have access to all payments. In this case such developed authorization would forbidden this user from accessing the resource. Would you build a separate API for such users? Commented Oct 22, 2021 at 22:47
2

I think the reason those resources state is because strictly speaking it negates the purpose of access tokens, on which existence OpenID was built upon. After all, ID tokens are intended exclusively for the client.

I have seen your approach before; the access tokens issued by Microsoft Teams on behalf of third party apps are just ID tokens provided with an additional scope claim. There are essentially treated interchangeably by design.

Since an ID token is guaranteed to be signed OpenID,

ID Tokens MUST be signed using JWS [JWS]

I see no issue regarding validating the authenticity of the token. You also mentioned the lifetime being configurable which allows to lower the lifetime accordingly if necessary. Yet, here are a few things to be considered (please note that this list might be incomplete):

The Subject-claim

The ID token's recipient is the client, while the access token is issued towards the resource server. I am not familiar with how AWS handles this but this might cause issues upon validation as the client id is usually different.

Encryption

For public client there are little to no options to encrypt ID tokens as the recipient can't store the corresponding secret. However, you can encrypt access tokens as they are usually merely used as Bearer tokens without valisation through the client. If you happen to use the ID token as both you lose that option (provided AWS even grants this).

Sender-constraints (the possible killer) There are additional mechanisms for client authentication such as Mutual-TLS or DPoP which are effectively based on the client possessing a secret to add additional authentication when passing the access token. Identity providers might not support this for ID tokens. I am not aware of this working for public clients anyway but am adding it for the sake of thoroughness.

0

The OpenIDConnect states that id_token is for the client (relay party) which would like to be sure that user is authenticated. But what does it mean? If you will take a look into how the id_token is generated (the authorization code flow with PKCE - this is the most recommended option currently), you will see that client (replay party) is generating some values (hashes) which are send to identity provider. Those values generated by client are also stored temporarily on client and then used to validate id_token.

Now the problem is that when you are sending id_token to resource server (API), it cannot properly validate it, as API do not have all required values to perform this process. For me that is the main security reason. (of course it is still dependent on the implementation).

4
  • Could you please elaborate this client (replay party) is generating some values (hashes) which are send to identity provider and after successful authentication are included into id_token. What values are we talking about? I don't see anything in the ID token coming from the client
    – eddyP23
    Commented Oct 21, 2021 at 7:48
  • Also, in our case, both ID and Access tokens are JWT tokens, to validate them you need the exact same information - the public key of the token issuer and without it, you cannot trust them
    – eddyP23
    Commented Oct 21, 2021 at 7:57
  • Before sending the authentication request client is creating code_verifier value and sends transformed value of code_verifier ( transf(code_verifier) ) to Authorization Server (AS). AS is saving this transformed value and proceed by standard way of authorization code flow. When client wants to perform the Access Token Request it sends previously generated code_verifier in request. AS is transforming received code_verifier and compares with value of transf(code_verifier). If is the same generates and sends back tokens. Details here: datatracker.ietf.org/doc/html/rfc7636 Commented Oct 22, 2021 at 22:34
  • Adding one more thing. In ID_token there is a claim c_hash which is holding a hashed value of code . Thanks to this client can validate if the token was generated by identity provider which generated the code Commented Oct 22, 2021 at 23:05
0

There is probably a reason why this hasn't been mentioned that I'm missing, but another solution is for the API to get the id_token from the /userinfo endpoint using the access_token:

Since in many cases it is desirable to retrieve additional user information at the API, this token is also valid for calling the /userinfo API, which will return the user's profile information. So the intended audience (aud claim) of the token is either the API (my-api-identifier) or the /userinfo endpoint (https://${account.namespace}/userinfo).

source: https://auth0.com/blog/why-should-use-accesstokens-to-secure-an-api/

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .