The Difference Between id_token and access_token in OpenID Connect

Introduction

An user will obtain a pair of tokens after authenticating with OpenID Connect. Why are there two tokens that seemingly do the same thing?

The token format and content is not defined by the Open ID connect standard. They can be anything. It’s common for both tokens to be equivalent, sometimes set to the exact same value, but it doesn’t have to be.

Think of OpenID Connect as an authentication framework, rather than a protocol. It can support any (existing) authentication system, with whatever (existing) token format.

There is a variety of providers and solutions: Gmail, Facebook, PingFederate, Forgerock, Microsoft Active Directory, etc… each one with its own idiosyncrasies.

As a developer who has to integrate one of these, you will have to understand what is coming out of that provider, only that one.

Authentication Response

The application receives a response like this upon a successful authentication.

{
    id_token: "2468407d-958f-401a-8244-3b63e3149445",
    access_token: "94ec6de9-5011-438e-86b7-6f7fa0031815",
}

Quick tip: That provider is giving random 128 bit session identifiers.

It could be a different format, like this instead.

{
    id_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJtYWlsIjoiYWxpY2VAZXhhbXBsZS5jb20ifQ.tbwhL417e4DBhZ5nQX1jZZBRV4HvulzG7Z7M00ukQtA",
    access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.L8i6g3PfcHlioHCCPURC9pmXT7gdJpx3kOoyAfNUwCc"
}

Quick tip: That provider is giving JWT tokens. JWT is a token format getting very popular lately.

The session token is to be saved as a cookie for a human in a browser, or passed as a header for programmatic access.

The question is which one is the session, if not both? We’re about to getting into that. (TLDR: Use the access_token by convention, not the id_token)

As a developer, there are 2 things to care about user authentication: Verifying that the token is valid/active and getting the associated user.

Use case: Black box tokens

A black box token is a blackbox. You do not know what it contains.

The provider MUST provide APIs to verify the token and to retrieve the user identity. Lookup documentation from the provider.

OpenID Connect has an optional “/userinfo” endpoint to retrieve user information, it’s a good starting point for a search. It may take a parameter to pick which user attributes to get (scope).

The API could accept either token, or both, try and see. Remember to read the documentation too!

You have to verify the token and retrieve the associated user. You’re done when you figure out what API to use and how.

{
    "id_token": "2468407d-958f-401a-8244-3b63e3149445",
    "access_token": "94ec6de9-5011-438e-86b7-6f7fa0031815"
}

Use case: JWT tokens

A JWT token is a javascript object containing user information, encoded in base64. Refer to https://jwt.io.

The token is cryptographically signed and can be verified using the public key from the authentication provider. There are many JWT libraries available to handle this.

{
    "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FkZnMubXljb21wYW55LmNvbS9hZGZzL3NlcnZpY2VzL3RydXN0IiwiZXhwIjoxNTE2MjM5MDIyLCJpYXQiOjE1MTYzMjU0MjIsIm1haWwiOiJleGFtcGxlQG15Y29tcGFueS5jb20ifQ.n-6j2gaY81RoK53mVTrB-lbvxpKg3FbFjfkgRqgM0No",
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FkZnMubXljb21wYW55LmNvbS9hZGZzL3NlcnZpY2VzL3RydXN0IiwiZXhwIjoxNTE2MjM5MDIyLCJpYXQiOjE1MTYzMjU0MjJ9.Ht60JatfFOf1d_nRg5YDeSrRm6zO17Dd96b2HGne4rg"
}

After decoding:

# id_token
{
    "alg": "HS256",
    "typ": "JWT",
    "iss": "https://adfs.mycompany.com/adfs/services/trust",
    "exp": 1516239022,
    "iat": 1516325422,
    "mail": "example@mycompany.com"
}
# access_token
{
    "alg": "HS256",
    "typ": "JWT",
    "iss": "https://adfs.mycompany.com/adfs/services/trust",
    "exp": 1516239022,
    "iat": 1516325422
}

Fields in order: algorithm, token type, issuer, expiration time, issued at time, user email. Refer to the JSON Web Token Claims standard by the IANA.

Suppose that the provider does NOT have any API to validate the token or to retrieve the user identity. It’s truly not needed with JWT tokens, everything needed can be embedded in the token.

As a developer, you need the user identifier. Typically a primary key, an email or an employee ID.

The two tokens are mostly equivalent, except for the mail field. Let’s assume that users are referenced by email in this example. The access_token is worthless here. It’s missing the email. It doesn’t indicate whom or what to grant access to?

OpenID Connect doesn’t define any required user attributes (claims). It’s up to the provider to decide what to set. Lookup the provider documentation on user attributes (claims).

You should have the opportunity to select claims, first when on boarding your OpenID Connect client with the OpenID Connect provider, then later by using a “scope” parameter in authentication APIs (scope=openid+profile+mail).

It’s up to the provider to accept and honor these configuration settings. It’s up to the provider what claims are supported and supportable (don’t except a programmatic account to have an email).

Ideally, you should configure all tokens to carry enough user attributes.

You’re done when you figure out which claim(s) you need and how to get them.

Quick tip: JWT can get fairly large and HTTP headers (including cookies) are limited to a few kilobytes. Limit claims to a minimum.

Quick tip: It would be great if the provider could have an appropriate user identifier built-in by default.

Use Case: Automatic Account Creation

There is a special use case that can be well handled by OpenID Connect. Creating accounts automatically for new users.

Let’s consider a practical example. A user is authenticating to Stack Overflow using Google (or StackOverflow Enterprise using Microsoft ADFS). (Plot twist: You can’t ask Stack Overflow for help before getting a Stack Overflow account).

StackOverflow is a standalone product with its own user base and session management. It should accept users from Google, Facebook, or on premise, yet it shouldn’t depend on them.

This is easily addressed in practice. The id_token was originally created for this exact purpose.

The user can initiate authentication with the third-party provider and come back with the tokens/claims described before, then:

  • If the external account matches an existing email or id, the user is logged in as that stack overflow account.
  • If the external account doesn’t match any internal account, a new account is created automatically. User information can be filled in automatically.

The id_token and the /userinfo endpoint can carry user attributes, either by default or when explicitly configured for it. What is supported is up to the provider.

Some providers serve the full user profile by default in order to cover this use case.

# notice the id_token is much longer
{
    "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FkZnMubXljb21wYW55LmNvbS9hZGZzL3NlcnZpY2VzL3RydXN0IiwiZXhwIjoxNTE2MjM5MDIyLCJpYXQiOjE1MTYzMjU0MjIsIm1haWwiOiJqb2huLmRvZUBteWNvbXBhbnkuY29tIiwibmFtZSI6IkpvaG4gRG9lIiwiZmlyc3RfbmFtZSI6IkpvaG4iLCJsYXN0X25hbWUiOiJEb2UiLCJwaG9uZV9udW1iZXIiOiIrNDQgKDApMTIzIDQ1NiA3ODkwIn0.lO8bEFTL7IWkPQhtNraJ3NvxiYVhITvOBh9WGkyCL_4",
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJtYWlsIjoiYWxpY2VAZXhhbXBsZS5jb20ifQ.tbwhL417e4DBhZ5nQX1jZZBRV4HvulzG7Z7M00ukQtA"
}

.

# from the id_token or the /userinfo endpoint
{
    "mail": "john.doe@mycompany.com",
    "name": "John Doe",
    "first_name": "John",
    "last_name": "Doe",
    "phone_number": "+44 (0)123 456 7890"
}

With all that in hands, it’s trivial to match an existing account or create a new one.

The user can login and enjoy a seamless experience with single-sign-on.

Quick tip: For this particular use case, tokens are only checked once to handle the login, no need to save them as cookies. Stack Overflow can create its own session token instead.

Conclusion

This covered the most common usages of id_token and access_token in the wild.

Your mileage may vary based on the OpenID Connect provider(s) you have to integrate with.

Popular internet providers should be fairly well documented and standardized (Google, Facebook, GitHub).

Enterprise solutions (ADFS, ForgeRock, Ping Identity, Auth0) can do anything and they’re all about tuning to the environment.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.