Token id что это
Перейти к содержимому

Token id что это

  • автор:

1. Introduction

Takahiko Kawasaki

From an engineer's point of view, an abstract explanation like ID Token is a token issued as a result of user authentication” is not so valuable because engineers cannot imagine how to implement ID Token at all by the explanation. Instead, what engineers want to know first, at least what I should have known before diving into reading OpenID Connect Core 1.0, is what an ID token looks like.

Knowledge about ID Token greatly helps engineers understand what OpenID Connect is. It's because “OpenID Connect is a specification as to how to request and issue ID tokens”.

2. What ID Token Looks Like

The following is an example of an ID token excerpted from “A.2. Example using response_type=id_token” in OpenID Connect Core 1.0.

In this example, there are two periods. One is the 9th character from the right on the 1st line. The other is the 22nd character from the right on the 9th line. By the two periods, an ID token is split into three fields. (The case where the number of fields is not 3 will be explained later.)

The three fields represent a header, a payload and a signature. That is, the format of an ID token is as follows:

In the case of the example excerpted above, the header, the payload and the signature are as follows:

Header

Payload

Signature

3. JSON Web Signature (JWS)

The format of Header.Payload.Signature shown in the previous section is the format defined in “7.1. JWS Compact Serialization" in RFC 7515, JSON Web Signature (JWS). The concrete definition described in the specification is as follows:

The definition tells that each field is encoded by base64url (RFC 4648, 5. Base 64 Encoding with URL and Filename Safe Alphabet). This means that we can get original contents by decoding the fields by base64url. Let's do it.

3.1. Decoding JWS Header

First, let's decode the header field. Here I install base64-url-cli and decode the header on the command line.

As a result, we can get this JSON.

The JSON includes two parameters, kid and alg . Parameters that a JWS header may include are listed in “4.1. Registered Header Parameter Names” in RFC 7515.

Among the parameters, alg is important. It represents the algorithm of the signature.

3.2. Decoding JWS Payload

Next, let's decode the payload field.

The output will become as follows:

The content of this JSON will be explained later, but one note: RFC 7515 does not require the content be JSON. The specification says the content can be “an arbitrary sequence of octets”.

3.3. Decoding JWS Signature

The alg parameter in the header represents the algorithm of the signature. Valid values of alg are listed not in RFC 7515 but in “3.1. "alg" (Algorithm) Header Parameter Values for JWS” in RFC 7518, JSON Web Algorithms (JWA).

The result of decoding the header says that the value of alg is RS256 . Referring to the table in “3.1. “alg” (Algorithm) Header Parameter Values for JWS”, we can find RS256 represents “RSASSA-PKCS1-v1_5 using SHA-256”.

Because the signature by RSA algorithm is binary data, even if we decode the signature field by base64url, what we can get is binary data.

3.4. Input for Signature

Input data for signature is Header.Payload . In the case of the example we've used so far, the input data is as follows:

3.5. Summary of Decoding JWS

3.6. Unsecured JWS

Unsecured JWS is a JWS without a signature. Below is an example excerpted from “A.5. Example Unsecured JWS” in RFC 7515.

The format of unsecured JWS is as follows. The signature field is empty.

Because a signature is missing, any algorithm for a signature is not needed. Therefore, if we decode the header field of an unsecured JWS,

the output tells that the algorithm is none .

4. JSON Web Encryption (JWE)

In addition to the format of Header.Payload.Signature , there is one more format for ID Token which has 5 fields as shown below.

This format is defined in “7.1. JWE Compact Serialization” in RFC 7516, JSON Web Encryption (JWE). The concrete definition described in the specification is as follows:

This format is used when ID tokens need to be encrypted.

Data are encrypted and then put in the 4th field. RFC 7516 says that any data can be used as the input. However, in the context of ID Token, the data has to be a JWS. That is, a JWS is nested in a JWE.

4.1. Two-Step Encryption

According to “7.1. JWE Compact Serialization”, the 2nd field of a JWE (in the format of compact serialization) is “JWE Encrypted Key”. It's not “Encryption Key” but “Encrypted Key”. There is a reason for this.

This method is not limited to JWE but used in various places; sometimes encryption is performed two times as follows.

  1. Encrypt data using a shared symmetric key.
  2. Encrypt the key used above using another different asymmetric key.

By using two-step encryption, we can minimize the time needed to encrypt data by using a symmetric key while we can benefit from advantages of asymmetric encryption. It should be noted that the shared key used in two-step encryption doesn't have to be shared in advance and that the party which encrypts data can generate the shared key randomly. It is because, even if the shared key is generated randomly, if the shared key is encrypted by an asymmetric key and passed to the opposite party, the shared key can be decrypted on the opposite party's side by using the paired asymmetric key.

The diagram below illustrates two-step encryption and the animation version of the diagram is here. JWE Encrypted Key is the “encrypted shared key” in the diagram.

4.2. Encryption Algorithms

JWE uses two-step encryption. Therefore, two encryption algorithms are used.

On the other hand, algorithms used to encrypt shared keys are listed in “4.1. "alg" (Algorithm) Header Parameter Values for JWE” in RFC 7518.

4.3. Algorithm “ dir ”

Among the identifiers of algorithms for key encryption, “ dir ” is special because it is not two-step encryption but direct encryption using a shared key. “4.5. Direct Encryption with a Shared Symmetric Key” in RFC 7518 states as follows:

This section defines the specifics of directly performing symmetric key encryption without performing a key wrapping step.

It should be noted that RFC 7518 does not define any rule as to how to decide the shared key but that OpenID Connect Core 1.0 defines a rule in “10.2. Encryption” as follows:

Symmetric Encryption
The symmetric encryption key is derived from the client_secret value by using a left truncated SHA-2 hash of the octets of the UTF-8 representation of the client_secret . For keys of 256 or fewer bits, SHA-256 is used; for keys of 257-384 bits, SHA-384 is used; for keys of 385-512 bits, SHA-512 is used. The hash value MUST be left truncated to the appropriate bit length for the AES key wrapping or direct encryption algorithm used, for instance, truncating the SHA-256 hash to 128 bits for A128KW . If a symmetric key with greater than 512 bits is needed, a different method of deriving the key from the client_secret would have to be defined by an extension. Symmetric encryption MUST NOT be used by public (non-confidential) Clients because of their inability to keep secrets.

4.4. JWE Example

The following is a JWE example excerpted from “A.1.7. Complete Representation” in RFC 7516. Newlines in the example exist there just for readability and an actual JWE does not include any newline.

Note that this JWE is not an ID token. The embedded data before being encrypted is this text: “The true sign of intelligence is not knowledge but imagination.”

4.5. Decoding JWE Header

Among the 5 fields of JWE, the first one is a JWE header. It is encoded by base64url but not encrypted, so let's decode it.

We will get this output:

alg is an algorithm for key encryption, and enc is an algorithm for data encryption. Parameters that a JWE header may include are listed in “4.1. Registered Header Parameter Names” in RFC 7516.

5. JSON Web Token (JWT)

As we have learned JWS and JWE, we are ready to learn JWT.

First, regarding the pronunciation of JWT, the specification, RFC 7519, JSON Web Token (JWT), states as follows in “Introduction”:

The suggested pronunciation of JWT is the same as the English word “jot”.

To put it simply, JWT is either JWS or JWE which contains a collection of “claims” in JSON format.

Before explaining what “claims” are, let me explain how the JSON (a collection of “claims”) is embedded in JWS or JWE.

5.1. JWT in JWS format

In the case of “JWT in JWS format”, a collection of claims in JSON format is encoded by base64url and then put in the 2nd field of JWS.

5.2. JWT in JWE format

In the case of “JWT in JWE format”, a collection of claims in JSON format is first encrypted, then encoded by base64url, and finally put in the 4th field of JWE.

5.3. Nested JWT

How can we do both signing and encrypting? It can be achieved by either wrapping a JWS in a JWE or wrapping a JWE in a JWS. JWT which wraps JWS or JWE inside it is called “Nested JWT”. The figure below illustrates a Nested JWT in the pattern of “JWS in JWE”.

ID Token is a kind of JWT. In the context of ID Token, signing is mandatory. As a result, ID Token is verifiable. On the other hand, encrypting is optional, but if encryption is performed, the order must be “signed and then encrypted” as stated in “2. ID Token” in OpenID Connect Core 1.0.

ID Tokens MUST be signed using JWS and optionally both signed and then encrypted using JWS and JWE respectively, thereby providing authentication, integrity, non-repudiation, and optionally, confidentiality, per Section 16.14. If the ID Token is encrypted, it MUST be signed then encrypted, with the result being a Nested JWT, as defined in JWT.

Therefore, if an ID token is encrypted, its format is “JWS in JWE” as just illustrated in the figure above.

5.4. JWT Claims

JWT contains a collection of “claims”. Claims in this context are pieces of information each of which is represented as a key-value pair in JSON format. Therefore, a collection of claims looks like the following.

The JSON below is an example of a collection of claims excerpted from “3.1. Example JWT” in RFC 7519.

RFC 7519 defines some standard claim names in “4.1. Registered Claim Names”.

  • iss — Issuer
  • sub — Subject
  • aud — Audience
  • exp — Expiration
  • nbf — Not Before
  • iat — Issued At
  • jti — JWT ID

As shown in the example excerpted from “3.1. Example JWT”, non-standard claims can be included in a JWT, but consideration should be given so that claim names don't conflict.

You might be surprised, but none of the standard claims is mandatory. As described in the second paragraph in “4. JWT Claims” as below, it is application-dependent which claims are mandatory or not.

The set of claims that a JWT must contain to be considered valid is context dependent and is outside the scope of this specification. Specific applications of JWTs will require implementations to understand and process some claims in particular ways. However, in the absence of such requirements, all claims that are not understood by implementations MUST be ignored.

From a viewpoint of RFC 7519, ID Token defined in OpenID Connect Core 1.0 is one of application examples of JWT. In the context of ID Token, some of the standard claims defined in RFC 7519 are mandatory. To be concrete, iss , sub , aud , exp , and iat are mandatory.

6. ID Token

As we have learned JWT, we are ready to learn ID Token.

Once again, ID Token is a kind of JWT.

The first paragraph of “2. ID Token” in OpenID Connect Core 1.0 says as follows:

The primary extension that OpenID Connect makes to OAuth 2.0 to enable End-Users to be Authenticated is the ID Token data structure. The ID Token is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when using a Client, and potentially other requested Claims. The ID Token is represented as a JSON Web Token (JWT).

ID Token contains claims about user authentication and other claims. Main claims are explained in “2. ID Token” and “5.1. Standard Claims” in OpenID Connect Core 1.0. In addition, “3.3.2.11. ID Token” defines at_hash claim and c_hash claim.

Let's look into the specifications of claims one by one.

6.1. iss Claim

The “iss” (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The “iss” value is a case-sensitive string containing a StringOrURI value. Use of this claim is OPTIONAL.

REQUIRED. Issuer Identifier for the Issuer of the response. The iss value is a case sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components.

iss is a claim to identify the issuer of the JWT. In RFC 7519, the value of iss is a string or a URI, but OpenID Connect Core 1.0 has added more requirements and the value must be a URL which starts with https:// without query and fragment components.

The following is a valid example as a value of iss .

A server which issues ID tokens (= OpenID provider) should use only domains which the server eligibly manages in order to avoid conflicts.

In addition, if you want to support OpenID Connect Discovery 1.0, be careful when you decide the value of iss because the server has to be able to accept HTTP requests at /.well-known/openid-configuration . For example, if the value of iss is https://example.com , the URL below has to be able to accept HTTP GET requests and return an appropriate JSON.

As a reference, a real example by Google is here:

6.2. sub Claim

The “sub” (subject) claim identifies the principal that is the subject of the JWT. The claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The “sub” value is a case-sensitive string containing a StringOrURI value. Use of this claim is OPTIONAL.

REQUIRED. Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User, which is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4 . It MUST NOT exceed 255 ASCII characters in length. The sub value is a case sensitive string.

sub claim represents the identifier of the authenticated user. In RFC 7519, the value of sub is a string or a URI, and OpenID Connect Core 1.0 says it must not exceed 255 ASCII characters in length.

An ID token is issued as a result of user authentication, and the identifier of the authenticated user is included in the ID token as the value of the sub claim.

6.3. aud Claim

The “aud” (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the “aud” claim when this claim is present, then the JWT MUST be rejected. In the general case, the “aud” value is an array of case-sensitive strings, each containing a StringOrURI value. In the special case when the JWT has one audience, the “aud” value MAY be a single case-sensitive string containing a StringOrURI value. The interpretation of audience values is generally application specific. Use of this claim is OPTIONAL.

REQUIRED. Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an array of case sensitive strings. In the common special case when there is one audience, the aud value MAY be a single case sensitive string.

aud claim lists expected receivers of the JWT. In the case of ID Token, aud claim includes the client ID of the client application which has requested the ID token.

6.4. exp Claim

The “exp” (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the “exp” claim requires that the current date/time MUST be before the expiration date/time listed in the “exp” claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.

REQUIRED. Expiration time on or after which the ID Token MUST NOT be accepted for processing. The processing of this parameter requires that the current date/time MUST be before the expiration date/time listed in the value. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value is a JSON number representing the number of seconds from 1970–01–01T0:0:0Z as measured in UTC until the date/time. See RFC 3339 for details regarding date/times in general and UTC in particular.

exp claim denotes when the JWT will expire. The value is represented as seconds since the Unix epoch (1970-Jan-01). Note that the unit is not milliseconds but seconds.

6.5. iat Claim

The “iat” (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.

REQUIRED. Time at which the JWT was issued. Its value is a JSON number representing the number of seconds from 1970–01–01T0:0:0Z as measured in UTC until the date/time.

iat claim denotes when the JWT was issued. The value is represented as seconds since the Unix epoch as the value of exp is.

6.6. auth_time Claim

Time when the End-User authentication occurred. Its value is a JSON number representing the number of seconds from 1970–01–01T0:0:0Z as measured in UTC until the date/time. When a max_age request is made or when auth_time is requested as an Essential Claim, then this Claim is REQUIRED; otherwise, its inclusion is OPTIONAL. (The auth_time Claim semantically corresponds to the OpenID 2.0 PAPE auth_time response parameter.)

auth_time claim denotes when the user was authenticated. The value is represented as seconds since the Unix epoch. This claim does not exist in RFC 7519.

User authentication is not always performed at the same timing when an ID token is requested. If the user has already logged in, the step of user authentication may be skipped on issuing an ID token. In this case, the time of user authentication ( auth_time ) and the time of ID token issue ( iat ) are different.

Inclusion of the auth_time claim is mandatory when any one of the following conditions is satisfied:

  • The request for the ID token includes a max_age request parameter.
  • The value of require_auth_time of the client application is true . (See “2. Client Metadata” in “OpenID Connect Dynamic Client Registration 1.0” for details)

6.7. nonce Claim

String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case sensitive string.

ID token requests may come with a nonce request parameter to protect from replay attacks. When the request parameter is included, the server will embed a nonce claim in the issued ID token with the same value of the request parameter.

6.8. acr Claim

OPTIONAL. Authentication Context Class Reference. String specifying an Authentication Context Class Reference value that identifies the Authentication Context Class that the authentication performed satisfied. The value “0” indicates the End-User authentication did not meet the requirements of ISO/IEC 29115 level 1. Authentication using a long-lived browser cookie, for instance, is one example where the use of “level 0” is appropriate. Authentications with level 0 SHOULD NOT be used to authorize access to any resource of any monetary value. (This corresponds to the OpenID 2.0 OpenID 2.0 PAPE nist_auth_level 0.) An absolute URI or an RFC 6711 registered name SHOULD be used as the acr value; registered names MUST NOT be used with a different meaning than that which is registered. Parties using this claim will need to agree upon the meanings of the values used, which may be context-specific. The acr value is a case sensitive string.

acr claim denotes the authentication contexts that the user authentication has satisfied.

6.9. amr Claim

OPTIONAL. Authentication Methods References. JSON array of strings that are identifiers for authentication methods used in the authentication. For instance, values might indicate that both password and OTP authentication methods were used. The definition of particular values to be used in the amr Claim is beyond the scope of this specification. Parties using this claim will need to agree upon the meanings of the values used, which may be context-specific. The amr value is an array of case sensitive strings.

amr claim denotes the methods of the user authentication. In the specification of OpenID Connect Core 1.0, values for the amr claim are not defined. But in June 2017, RFC 8176 (Authentication Method Reference Values) that standardizes some values of the amr claim was released.

6.10. azp Claim

OPTIONAL. Authorized party — the party to which the ID Token was issued. If present, it MUST contain the OAuth 2.0 Client ID of this party. This Claim is only needed when the ID Token has a single audience value and that audience is different than the authorized party. It MAY be included even when the authorized party is the same as the sole audience. The azp value is a case sensitive string containing a StringOrURI value.

azp claim denotes the authorized party. To be concrete, the value of the claim is the client ID which has requested the ID token.

6.11. User Attribute Claims

Claims related to user attributes are defined in “5.1. Standard Claims” in OpenID Connect Core 1.0.

  • sub — identifier of the user
  • name — full name
  • given_name — given name
  • family_name — family name
  • middle_name — middle name
  • nickname — nickname
  • preferred_username — name by which the user wishes to be referred to
  • profile — URL of a profile page
  • picture — URL of a profile picture
  • website — URL of a web/blog site
  • email — email address
  • email_verified — whether the email has been verified
  • gender — gender
  • birthdate — birthday in YYYY-MM-DD format
  • zoneinfo — timezone; e.g. Europe/Paris
  • locale — locale; e.g. en-US
  • phone_number — phone number
  • phone_number_verified — whether the phone number has been verified
  • address — postal address; the format is defined in “5.1.1. Address Claim”
  • updated_at — last time the user's information was updated at

Some claims listed above can have localized values. For example, in the case of family_name , “Kawasaki” in English can be localized to “川崎” (Japanese Kanji) and “カワサキ” (Japanese Katakana). To support localization of claims, “#LanguageTag” can be appended after claim names. Details are described in “5.2. Claims Languages and Scripts”.

The following are examples of claims with a language tag.

As for “Language Tag”, please refer to RFC 5646 (Tags for Identifying Languages).

6.12. Hash Claims

Depending on the value of the response_type request parameter, an access token and/or an authorization code may be issued together with an ID token (See “Diagrams of All The OpenID Connect Flows” for details). In this case, at_hash claim and/or c_hash claim may be added to the ID token. Under some conditions, the claims are mandatory. Details are described in “3.3.2.11. ID Token”.

at_hash

Access Token hash value. Its value is the base64url encoding of the left-most half of the hash of the octets of the ASCII representation of the access_token value, where the hash algorithm used is the hash algorithm used in the alg Header Parameter of the ID Token's JOSE Header. For instance, if the alg is RS256 , hash the access_token value with SHA-256, then take the left-most 128 bits and base64url encode them. The at_hash value is a case sensitive string. If the ID Token is issued from the Authorization Endpoint with an access_token value, which is the case for the response_type value code id_token token , this is REQUIRED; otherwise, its inclusion is OPTIONAL.

c_hash

Code hash value. Its value is the base64url encoding of the left-most half of the hash of the octets of the ASCII representation of the code value, where the hash algorithm used is the hash algorithm used in the alg Header Parameter of the ID Token's JOSE Header. For instance, if the alg is HS512 , hash the code value with SHA-512, then take the left-most 256 bits and base64url encode them. The c_hash value is a case sensitive string. If the ID Token is issued from the Authorization Endpoint with a code , which is the case for the response_type values code id_token and code id_token token , this is REQUIRED; otherwise, its inclusion is OPTIONAL.

In addition, Financial API (which is being discussed and defined by Financial API Working Group of OpenID Foundation) has added s_hash claim. It is defined in “5.1. Introduction” in Financial API Part 2.

s_hash

State hash value. Its value is the base64url encoding of the left-most half of the hash of the octets of the ASCII representation of the state value, where the hash algorithm used is the hash algorithm used in the alg Header Parameter of the ID Token’s JOSE Header. For instance, if the alg is HS512 , hash the code value with SHA-512, then take the left-mots 256 bits and base64url encode them. The s_hash value is a case sensitive string.

Summary

  1. ID Token is a kind of JWT.
  2. ID Token is always signed and optionally encrypted.
  3. ID Token conveys a collection of claims in a verifiable manner.
  4. Claims are mainly related to user authentication and user attributes.

Finally

If you are looking for an implementation of OpenID provider, please consider Authlete. Read New Architecture of OAuth 2.0 and OpenID Connect implementation, and you will love the architecture of Authlete ��

Types of tokens in oidc and oauth

Understanding id_token, access_token and refresh_token

## OIDC, oAuth2.0, JWT, RS256, HS256, Tokens — don’t let the jargons be a barrier!

Key points

id_token

  • An id_token is a JWT — make note of that!
  • It contains claims about the identity of the user/resource owner
  • Having a valid id_token means that the user is authenticated

access_token

  • An access_token is a bearer token
  • A bearer token means that the bearer can access the resource without further identification
  • An access_token can be a JWT (see Appendix point 1.) or opaque

refresh_token

  • Are long lived opaque tokens used to obtain new access_token

Details

id_token is typically used for:

  • Stateless sessions: The presence of a valid id_token (jwt) provides information about the session. e.g. For browsers, cookies can store id_token
  • User information: The client app receives claims about the user as part of the id_token
  • Token exchange: The ID token may be exchanged for an access token at the token endpoint of an OAuth 2.0 authorization server. (see Appendix 3.)
  • Has one and only one use, i.e. to establish which resource the bearer has access to
  • The token is meant to be opaque to the client application
  • But for the resource server, the access_token maps to the scope or claims that the bearer has
  • If this is a jwt, it may contain scope as part of the token; which the resource server can verify on the fly (without introspection)

If you are looking for a practical example on how jwt are used as access_tokens, I wrote an authentication-authorization piece for graphql here: https://github.com/mannharleen/graphql-auth-implementation

The story of scopes & claims

Remember: Claims are grouped under Scope. e.g. claims ‘email’, ‘name’, ‘age’, etc. are part of the scope called ‘profile’

scope

A scope is a space separated list of identifiers that specify what access privileges are being requested by the client application

ID Token and Access Token: What's the Difference?

Learn what ID and access tokens are and how to correctly use them in the OpenID Connect and OAuth context.

Last Updated On: October 28, 2021

OAuth2 And OpenID Connect: The Professional Guide

"Let’s use a token to secure this API call. Should I use the ID token or the access token? ​�� The ID token looks nicer to me. After all, if I know who the user is, I can make better authorization decisions, right?"

Have you ever found yourself making similar arguments? Choices based on your intuition may sound good, but what looks intuitive is not always correct. In the case of ID and access tokens, they have clear and well-defined purposes, so you should use them based on that. Using the wrong token can result in your solution being insecure.

"What changes after all? They are just tokens. I can use them as I see fit. What’s the worst that could happen?"

Let’s take a closer look at these two types of tokens to better understand their role in authentication and authorization processes.

If you prefer, you can also watch this video on the same topic:

What Is an ID Token?

An ID token is an artifact that proves that the user has been authenticated. It was introduced by OpenID Connect (OIDC), an open standard for authentication used by many identity providers such as Google, Facebook, and, of course, Auth0. Check out this document for more details on OpenID Connect. Let's take a quick look at the problem OIDC wants to resolve.

Consider the following diagram:

Here, a user with their browser authenticates against an OpenID provider and gets access to a web application. The result of that authentication process based on OpenID Connect is the ID token, which is passed to the application as proof that the user has been authenticated.

This provides a very basic idea of what an ID token is: proof of the user's authentication. Let’s see some other details.

An ID token is encoded as a JSON Web Token (JWT), a standard format that allows your application to easily inspect its content, and make sure it comes from the expected issuer and that no one else changed it. If you want to learn more about JWTs, check out The JWT Handbook.

To put it simply, an example of ID token looks like this:

Of course, this isn't readable to the human eye, so you have to decode it to see what content the JWT holds. By the way, the ID token is not encrypted but just Base 64 encoded. You can use one of the many available libraries to decode it, or you can examine it yourself with the jwt.io debugger.

Without going deeper into the details, the relevant information carried by the ID token above looks like the following:

These JSON properties are called claims, and they are declarations about the user and the token itself. The claims about the user define the user’s identity.

Actually, the OpenID Connect specifications don't require the ID token to have user's claims. In its minimal structure, it has no data about the user; just info about the authentication operation.

One important claim is the aud claim. This claim defines the audience of the token, i.e., the web application that is meant to be the final recipient of the token. In the case of the ID token, its value is the client ID of the application that should consume the token.

Remember this small detail about the audience claim because it will help you better understand what its correct use is later on.

The ID token may have additional information about the user, such as their email address, picture, birthday, and so on.

Finally, maybe the most important thing: the ID token is signed by the issuer with its private key. This guarantees you the origin of the token and ensures that it has not been tampered with. You can verify these things by using the issuer's public key.

Cool! Now you know what an ID token is. But what can you do with an ID token?

First, it demonstrates that the user has been authenticated by an entity you trust (the OpenID provider) and so you can trust the claims about their identity.

Also, your application can personalize the user’s experience by using the claims about the user that are included in the ID token. For example, you can show their name on the UI, or display a "best wishes" message on their birthday. The fun part is that you don’t need to make additional requests, so you may get a little gain in performance for your application.

What Is an Access Token?

Now that you know what an ID token is, let’s try to understand what an access token is.

Let's start by depicting the scenario where the access token fits:

Access token scenario

In the diagram above, a client application wants to access a resource, e.g., an API or anything else which is protected from unauthorized access. The other two elements in that diagram are the user, which is the owner of the resource, and the authorization server. In this scenario, the access token is the artifact that allows the client application to access the user's resource. It is issued by the authorization server after successfully authenticating the user and obtaining their consent.

In the OAuth 2 context, the access token allows a client application to access a specific resource to perform specific actions on behalf of the user. That is what is known as a delegated authorization scenario: the user delegates a client application to access a resource on their behalf. That means, for example, that you can authorize your LinkedIn app to access Twitter’s API on your behalf to cross-post on both social platforms. Keep in mind that you only authorize LinkedIn to publish your posts on Twitter. You don't authorize it to delete them or change your profile’s data or do other things, too. This limitation is very important in a delegated authorization scenario and is achieved through scopes. Scopes are a mechanism that allows the user to authorize a third-party application to perform only specific operations.

Of course, the API receiving the access token must be sure that it actually is a valid token issued by the authorization server that it trusts and make authorization decisions based on the information associated with it. In other words, the API needs to somehow use that token in order to authorize the client application to perform the desired operation on the resource.

How the access token should be used in order to make authorization decisions depends on many factors: the overall system architecture, the token format, etc. For example, an access token could be a key that allows the API to retrieve the needed information from a database shared with the authorization server, or it can directly contain the needed information in an encoded format. This means that understanding how to retrieve the needed information to make authorization decisions is an agreement between the authorization server and the resource server, i.e., the API.

OAuth 2 core specifications say nothing about the access token format. It can be a string in any format. A common format used for access tokens is JWT, and a standard structure is available. However, this doesn’t mean that access tokens should be in that format.

Alright! Now you know what an ID token and an access token are. �� So you are ready to use them without any fear of making mistakes. But, wait. I do not see you convinced. �� Maybe you need some other information. Ok. So, let’s see what these tokens are not suitable for.

What Is an ID Token NOT Suitable For?

One of the most common mistakes developers make with an ID token is using it to call an API.

As said above, an ID token proves that a user has been authenticated. 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.

However, even in this scenario, the security of your application, consisting of the client and the API, may be at risk. In fact, 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.

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. 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.

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. In addition to the lack of mechanisms to bind it to the client, there are several other reasons not to do this.

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. That claim says that it is meant for your client application, not for the resource server (i.e., the API).

You may think this is just a formality, but there are security implications here.

First of all, among other validation checks, your 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. Of course, checking the audience is just one of the checks that your API should do to prevent unauthorized access.

In addition, your ID token will not have granted scopes (I know, this is another pain point). As said before, scopes allow the user to restrict the operations your client application can do on their behalf. Those scopes are associated with the access token so that your API knows what the client application can do and what it can't do. If your client application uses an ID token to call the API, you ignore this feature and potentially allow the application to perform actions that the user has not authorized.

What Is an Access Token NOT Suitable For?

On the access token side, it was conceived to demonstrate that you are authorized to access a resource, e.g., to call an API.

Your client application should use it only for this reason. In other words, the access token should not be inspected by the client application. It is intended for the resource server, and your client application should treat access tokens as opaque strings, that is, strings with no specific meaning. Even if you know the access token format, you shouldn’t try to interpret its content in your client application. As said, the access token format is an agreement between the authorization server and the resource server, and the client application should not intrude. Think of what can happen if one day the access token format changes. If your client code was inspecting that access token, now it will break unexpectedly.

A Quick Recap

The confusion over the use of ID and access tokens is very common, and it can be difficult to wrap your head around the differences. Maybe it mostly derives from not having a clear understanding of the different goals of each artifact as defined by the OAuth and OpenID Connect specifications. Also, understanding the scenarios where those artifacts were originally meant to operate has an important role in preventing confusion on their use. Nevertheless, I hope this topic is a little more clear now.

To recap, here is a quick summary of what you learned about what you can and can’t do with ID and access tokens:

ID token vs access token

If you want to see ID and access tokens in action, sign up for a free Auth0 account and start to add authentication and authorization to your applications in minutes with your preferred programming language and framework.

Andrea Chiarelli

Principal Developer Advocate

I have over 20 years of experience as a software engineer and technical author. Throughout my career, I've used several programming languages and technologies for the projects I was involved in, ranging from C# to JavaScript, ASP.NET to Node.js, Angular to React, SOAP to REST APIs, etc.

In the last few years, I've been focusing on simplifying the developer experience with Identity and related topics, especially in the .NET ecosystem.

Andrea Chiarelli

Principal Developer Advocate

I have over 20 years of experience as a software engineer and technical author. Throughout my career, I've used several programming languages and technologies for the projects I was involved in, ranging from C# to JavaScript, ASP.NET to Node.js, Angular to React, SOAP to REST APIs, etc.

In the last few years, I've been focusing on simplifying the developer experience with Identity and related topics, especially in the .NET ecosystem.

More like this

Follow the conversation

Powered by the Auth0 Community. Sign up now to join the discussion. Community links will open in a new window.

Иллюстрированное руководство по OAuth и OpenID Connect

Прим. перев.: В этом замечательном материале компании Okta просто и наглядно рассказывается о принципах работы OAuth и OIDC (OpenID Connect). Эти знания будут полезны разработчикам, системным администраторам и даже «обычным пользователям» популярных веб-приложений, которые скорее всего тоже обмениваются конфиденциальными данными с другими сервисами.

В «каменном веке» интернета делиться информацией между сервисами было легко. Вы просто давали свой логин и пароль от одного сервиса другому, чтобы тот вошел в вашу учетную запись и получил любую необходимую ему информацию.


«Предоставьте свою банковскую учётку». — «Обещаем, что с паролем и деньгами все будет в порядке. Вот прям честно-пречестно!» *хи-хи*

Жуть! Никто и никогда не должен требовать от пользователя поделиться логином и паролем, его учётными данными, с другим сервисом. Нет никакой гарантии, что организация, стоящая за этим сервисом, будет хранить данные в безопасности и не соберет больше персональной информации, чем нужно. Это может показаться дикостью, но некоторые приложения до сих пор применяют подобную практику!

Сегодня имеется единый стандарт, позволяющий одному сервису безопасно воспользоваться данными другого. К сожалению, подобные стандарты используют массу жаргонизмов и терминов, что усложняет их понимание. Цель этого материала — с помощью простых иллюстраций объяснить, как они работают (Думаете, что мои рисунки напоминают детскую мазню? Ну и ладно!).

Между прочим, это руководство также доступно в формате видео:

Дамы и господа, встречайте: OAuth 2.0

OAuth 2.0 — это стандарт безопасности, позволяющий одному приложению получить разрешение на доступ к информации в другом приложении. Последовательность действий для выдачи разрешения [permission] (или согласия [consent]) часто называют авторизацией [authorization] или даже делегированной авторизацией [delegated authorization]. С помощью этого стандарта вы позволяете приложению считать данные или использовать функции другого приложения от вашего имени, не сообщая ему свой пароль. Класс!

В качестве примера представим, что вы обнаружили сайт с названием «Неудачный каламбур дня» [Terrible Pun of the Day] и решили зарегистрироваться на нем, чтобы ежедневно получать каламбуры в виде текстовых сообщений на телефон. Сайт вам очень понравился, и вы решили поделиться им со всеми знакомыми. Ведь жуткие каламбурчики нравятся всем, не так ли?


«Неудачный каламбур дня: Слышали о парне, который потерял левую половину тела? Теперь он всегда прав!» (перевод примерный, т.к. в оригинале своя игра слов — прим. перев.)

Понятно, что писать каждому человеку из контакт-листа не вариант. И, если вы хотя бы чуточку похожи на меня, то пойдете на всё, чтобы избежать лишней работы. Благо Terrible Pun of the Day может сам пригласить всех ваших друзей! Для этого лишь нужно открыть ему доступ к электронной почте контактов — сайт сам отправит им приглашения (OAuth рулит)!


«Все любят каламбуры! — Уже залогинились? — Хотите открыть доступ сайту Terrible Pun of the Day к списку контактов? — Спасибо! Теперь мы каждый день будем слать напоминания всем, кого вы знаете, до скончания веков! Вы самый лучший друг!»

  1. Выберите свой сервис электронной почты.
  2. При необходимости перейдите на сайт почты и войдите в учетную запись.
  3. Дайте разрешение сайту Terrible Pun of the Day на доступ к контактам.
  4. Вернитесь на сайт Terrible Pun of the Day.

Поток OAuth

Только что мы прошли через то, что обычно называют потоком [flow] OAuth. В нашем примере этот поток состоит из видимых шагов, а также из нескольких невидимых шагов, в рамках которых два сервиса договариваются о безопасном обмене информацией. В предыдущем примере с Terrible Pun of the Day используется наиболее распространенный поток OAuth 2.0, известный как поток «с кодом авторизации» [«authorization code» flow].

Прежде чем углубиться в подробности работы OAuth, давайте поговорим о значении некоторых терминов:

Примечание: иногда Authorization Server и Resource Server являются одним и тем же сервером. Однако в некоторых случаях это могут быть разные серверы, даже не принадлежащие к одной организации. Например, Authorization Server может быть сторонним сервисом, которому доверяет Resource Server.

Теперь, когда мы ознакомились с основными понятиями OAuth 2.0, давайте вернемся к нашему примеру и подробно рассмотрим, что происходит в потоке OAuth.

  1. Вы, Resource Owner, желаете предоставить сервису Terrible Pun of the Day (Client‘у) доступ к своим контактам, чтобы тот мог разослать приглашения всем вашим друзьям.
  2. Client перенаправляет браузер на страницу Authorization Server‘а и включает в запрос Client ID, Redirect URI, Response Type и одно или несколько Scopes (разрешений), которые ему необходимы.
  3. Authorization Server проверяет вас, при необходимости запрашивая логин и пароль.
  4. Authorization Server выводит форму Consent (подтверждения) с перечнем всех Scopes, запрашиваемых Client‘ом. Вы соглашаетесь или отказываетесь.
  5. Authorization Server перенаправляет вас на сайт Client‘а, используя Redirect URI вместе с Authorization Code (кодом авторизации).
  6. Client напрямую связывается с Authorization Server‘ом (в обход браузера Resource Owner‘а) и безопасно отправляет Client ID, Client Secret и Authorization Code.
  7. Authorization Server проверяет данные и отвечает с Access Token‘ом (токеном доступа).
  8. Теперь Client может использовать Access Token для отправки запроса на Resource Server с целью получить список контактов.

Client ID и Secret

Задолго до того, как вы разрешили Terrible Pun of the Day получить доступ к контактам, Client и Authorization Server установили рабочие отношения. Authorization Server сгенерировал Client ID и Client Secret (иногда их называют App ID и App Secret) и отправил их Client’у для дальнейшего взаимодействия в рамках OAuth.

/>
«— Привет! Я хотел бы работать с тобой! — Да не вопрос! Вот твои Client ID и Secret!»

Название намекает, что Client Secret должен держаться в тайне, чтобы его знали только Client и Authorization Server. Ведь именно с его помощь Authorization Server подтверждает истинность Client’а.

Но это ещё не всё… Пожалуйста, поприветствуйте OpenID Connect!

OAuth 2.0 разработан только для авторизации — для предоставления доступа к данным и функциям от одного приложения другому. OpenID Connect (OIDC) — это тонкий слой поверх OAuth 2.0, добавляющий сведения о логине и профиле пользователя, который вошел в учетную запись. Организацию логин-сессии часто называют аутентификацией [authentication], а информацию о пользователе, вошедшем в систему (т.е. о Resource Owner‘е), — личными данными [identity]. Если Authorization Server поддерживает OIDC, его иногда называют поставщиком личных данных [identity provider], поскольку он предоставляет Client‘у информацию о Resource Owner‘е.

OpenID Connect позволяет реализовывать сценарии, когда единственный логин можно использовать во множестве приложений, — этот подход также известен как single sign-on (SSO). Например, приложение может поддерживать SSO-интеграцию с социальными сетями, такими как Facebook или Twitter, позволяя пользователям использовать учётную запись, которая у них уже имеется и которую они предпочитают использовать.

Поток (flow) OpenID Connect выглядит так же, как и в случае OAuth. Единственная разница в том, что в первичном запросе используемый конкретный scope — openid , — а Client в итоге получает как Access Token, так и ID Token.

Так же, как и в потоке OAuth, Access Token в OpenID Connect — это некое значение, не понятное Client‘у. С точки зрения Client‘а Access Token представляет некую строку из символов, которая передается вместе с каждым запросом к Resource Server‘у, а тот определяет, действителен ли токен. ID Token представляет собой совсем иное.

ID Token — это JWT

ID Token — это особым образом отформатированная строка символов, известная как JSON Web Token или JWT (иногда токены JWT произносят как «jots»). Сторонним наблюдателям JWT может показаться непонятной абракадаброй, однако Client может извлечь из JWT различную информацию, такую как ID, имя пользователя, время входа в учетную запись, срок окончания действия ID Token‘а, наличие попыток вмешательства в JWT. Данные внутри ID Token‘а называются заявками [claims].

В случае OIDC также имеется стандартный способ, с помощью которого Client может запросить дополнительную информацию о личности [identity] от Authorization Server‘а, например, адрес электронной почты, используя Access Token.

Дополнительные сведения об OAuth и OIDC

Итак, мы вкратце рассмотрели принципы работы OAuth и OIDC. Готовы копнуть глубже? Вот дополнительные ресурсы, которые помогут узнать больше об OAuth 2.0 и OpenID Connect:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *