Hallgeir
Hallgeir

Reputation: 1273

OpenID Connect: Proper way of authenticating user - ID token or Access token? Refreshing ID tokens?

In our web application (ASP.NET), we use OpenID Connect with the authorization code flow:

  1. The user is redirected to the identity provider (e.g. Azure AD), authenticates,
  2. The authorization code is POSTed back to a page in our web application.
  3. Our web app then retrieves the refresh token, id token and access token from the identity server using the authorization code. These are stored on the clients as cookies (with the HttpOnly flag set to true). This is in order to avoid a dependency on the server's state, in case the user is routed to a different web server by the load balancer.
  4. When the user accesses a page, we validate the ID token's signature and validity period, and checking the claim we use for the identity (e.g. email address or UPN) against the user database in our application.

This works -- except that we're unable to refresh the ID token, so users are timed out after 1 hour, requiring a new login. According to the OpenID Connect specs, when refreshing tokens with the token endpoint, not all OpenID Connect providers will supply a new ID token.

The alternatives we see so far:

  1. Don't use the ID token at all. Use the access token to query the UserInfo endpoint for the user's claims, and cache it on the server (on cache miss, e.g. if routed to a different web server - simply use the provided access token from the cookie to request the UserInfo again). Since the access tokens can be refreshed, this would probably work fine.
    • Pros: We get a properly refreshed token, that are validated by the server.
    • Cons: Not all claims (e.g. aud and iss) are provided by the UserInfo endpoint, at least for Azure AD.
  2. Don't verify expiry of the ID token, just that it's not older than e.g. 12 hours.
    • Pros: Simple, requires little effort to change from the current behavior. Has all the claims that we also have today.
    • Cons: Might be a security risk? Comments?

So what is the recommended way of persisting a user's login over a longer period of time? Would using the access token with the UserInfo endpoint be a suitable solution?

Upvotes: 3

Views: 2992

Answers (2)

user4864425
user4864425

Reputation:

It depends on how the Identity token is used. Usually it's only there for the client. It allows the client to authenticate the user based on the mandatory sub claim. It shouldn't be used for authorization. That's what the access token is for.

In the hybrid flow (code+id_token) you could check the authenticity of the user before requesting the other tokens. In that case a small token is most efficient.

In the authorization code flow (code) you'd have to request the tokens anyway using the code. It now depends on the information in the id_token. If id_token doesn't contain the profile claims then you'll need to request the information from the UserInfo endpoint.

In order to access the UserInfo endpoint you need the access token. The missing aud and iss claims may not be a problem as you request the token by the authority itself. Seems to me that the issuer and audience are known at that point.

Please note that the id_token isn't used for authorization, so there shouldn't be a security risk. Not even when the id_token is shared with resources.

The specs require you to validate the received token as described. But when you don't receive a new id_token, why validate the current one again? It may not be up to date but it's already validated.

So IMO both options are fine, though I think that the first option is more common. It's likely that the id_token is discarded after use. Because the access token is used for accessing the resources (including the UserInfo endpoint) and the refresh token for refreshing the access token.

One remark, the consent of the user is used as a filter. If the user doesn't give consent for profile information then it won't be available at all.

Upvotes: 3

Kavindu Dodanduwa
Kavindu Dodanduwa

Reputation: 13059

First you need to understand the purpose of each token. ID Token is there to be consumed by the client (application). It contains claims which you can validate to authenticate the end user (Authentication).

Access token is there for authorization. It is intended to be used against a protected resource (ex:- API protected by OAuth 2.0 tokens). When API receives the token, it must validate it and grant access.

Refresh token is there to refresh the access token. Now OpenID Connect being an extension of OAuth 2.0, allows the usage of refresh token. But as you figured out ID token may or may not be refreshed. I have personally seen Auzre AD not refreshing ID token.

Your best solution is to use a session between front end and backend. You set this session after you validate tokens from token request. Session storage can hold access token that was originally sent. Along with that, store the refresh token. You can simply validate the ID token and discard it (given that you store all required information to session). Make sure this session to be HttpOnly and Secure (Https only). Session expiration can be equal to Access token validity.

Once access token expires, you can use refresh token to obtain a new token. And whenever you want, your backend can append access token to API requests from backend. This way you never expose access token and refresh token to front end.

Upvotes: 5

Related Questions