Hosted Login Token Reference


We're Moving ... Again

Yes, the Identity Cloud documentation is once more on the move. And this time we're headed to two different locations:

As for the Education Center, well, the Center itself will disappear on July 23rd. We apologize for any inconvenience, but we look forward to becoming a full-fledged member of the Akamai family.
 

Token are small bits of digital information (typically encoded/encrypted as JSON objects) that are used for authentication and authorization purposes.

Note. So what is the difference between authentication and authorization? Good question, and here’s a quick answer: authentication is the process of verifying that you are who you say you are. For example, when you show your passport at the airport security line, that's authentication: you’re providing proof that you are Maria Fuentes. By comparison, authorization is the process of determining what you're allowed to do now that we know who you are. When you board the airplane and show your ticket to the flight attendant, he or she might tell you to go sit in seat 39E. That's authorization: based on who you are, you're allowed to sit in seat 39E, and only in seat 39E.

Prior to token-based authentication, session information for each logged-on user had to be maintained and updated on each and every server in a cluster; this not only led to scalability issues, but also opened the door to web attacks such as cross-origin resource sharing (CORS) and cross-site request forgery (CSRF). Token-based authentication largely does away with these problems, in part because authentication and access information is not maintained on each server. Instead, this information is included in the access token, and that token must accompany each and every request for resources. Because the token indicates what the user is allowed to do, servers no longer have to maintain a list of all the logged-on users and all their access privileges.

When working with OIDC and with Hosted Login, you will encounter four primary token types:

  • Access tokens
  • Refresh tokens
  • Identity tokens
  • Anti-forgery state tokens

In this documentation, we’ll describe each token type in more detail. In addition, we’ll also discuss token management tasks, including inspecting tokens, revoking tokens, and configuring token lifetimes.

Access Tokens and Refresh Tokens

Access Tokens

Access tokens let applications and websites know what you're allowed to do. Do you need access to a specific user profile? In that case, the server is going to check your access token to make sure you're actually allowed access to that user profile: you’ll get access only if your token says that you’re allowed access.

Actually, there's an important clarification we need to make here: the server will check to see if the token is allowed access to the user profile. As it turns out, access tokens are "bearer tokens," which means they provide access to anyone who has possession of (i.e., anyone who bears) that token. No ID checks are performed when you access a resource; instead, possession of the token is all that matters. It's a bit like showing up at a baseball game and giving the ticket-taker your ticket. The ticket-taker isn’t going to ask for your ID; instead, he or she is simply going to verify that the ticket is valid (for example, that it's not a ticket for tomorrow's game or for yesterday's game). If the ticket is valid, they'll let you into the stadium.

Is this important to know? Yes, it is. Because no identity checks are made, it's crucial that you keep your access tokens secure. After all, if a token "leaks out," anyone who grabs that token can use it to carry out any of the actions the token is allowed to carry out. One way to help keep a token secure? Make sure that your tokens only travel on HTTPS networks, and never on HTTP networks. Another best practice? Whenever possible, delete access tokens and refresh tokens any time a user logs off from your website or application.

Because access tokens are bearer tokens, these tokens also have a very short lifespan: if a token does leak out, there’s only a limited amount of time (by default, 1 hour) in which the token could be used for malicious purposes.

Refresh Tokens

As noted, access tokens typically have a short lifespan: at best, you can stay logged on to a web site or an app for only an hour or so before your access token expires. (And what happens then? If your access token expires you won’t be allowed access to any protected resources until you obtain a new access token.)

In other words, if your access token is only good for 60 minutes (the Hosted Login default value), then, when your 60 minutes are up, you'll lose access to site resources. Or at least you wouldn’t be allowed access if it wasn't for refresh tokens. Refresh tokens provide a way for clients to stay logged on to a website or application for – well, for however long you want users to stay logged on. For example, suppose your organization configures refresh tokens to have a 7-day lifespan. That means that, by default, a user can log on to your site and stay there for 60 minutes, the lifespan of the access token. Just before those 60 minutes are up, however, the client can use a refresh token to request a new access token. By repeating this process over and over, the user can stay logged on indefinitely (each time the user redeems a refresh token not only do they get a new access token but the "refresh token clock" resets itself to 7 days).

Important. So could a user log on once and then stay logged on forever? In theory, yes. However, you can use the max_age parameter to set a time limit on user sessions. That would force a user to reauthenticate even if he or she still has valid access and refresh tokens.

If you're wondering, "Why don't we just make our access tokens last for 7 days?" well, some organizations do just that (although that’s something you can’t do in Hosted Login: Hosted Login imposes a maximum lifetime of 60 minutes for access tokens). However, using a combination of access tokens and refresh tokens offers some security advantages to using nothing but long-lived access tokens. After all, if a hacker steals an access token they'll have a limited amount of time (e.g., 60 minutes) in which to use that token. Because of that, a hacker really needs to hijack both the access token and the refresh token, which means that you’ve already made his or her efforts twice as difficult.

Access and Refresh Token Properties

Access tokens and refresh tokens are encoded strings that look similar to this:

03v-eeodppPrrHXXIx56pRLyDBaOldDxqEwI59MFCFGVuSkLRapzgmfwmEHyKWle

Admittedly, a value like that isn’t of much use to a human being, which is fine: you rarely need to know the actual contents of a token. But what if you do need to know the actual contents of an access token or a refresh token; are you just out of luck?

As it turns out, no, you’re not out of luck. In fact, you can easily decipher a token (access or refresh tokens only) by calling the /login/token/introspect endpoint. For example, here’s a Curl command that returns the information associated with the token 03v-eeodppPrrHXXIx56pRLyDBaOldDxqEwI59MFCFGVuSkLRapzgmfwmEHyKWle:

curl -X POST \
  https://v1.api.us.janrain.com/00000000-0000-0000-0000-000000000000/login/token/introspect \
  -H 'Authorization: Basic RcaWTi0woO52rqZjlbApm2lL3Aokzd1bhCZZajX51aX4IQrH1Uj1D4ks9HfJtxoRI7HCsyNVoc6Qj4oBfuplftc7tMbR26eZHwtEqaw9RLMBeIJDvqvqyD4l' \
  -d 'token=03v-eeodppPrrHXXIx56pRLyDBaOldDxqEwI59MFCFGVuSkLRapzgmfwmEHyKWle&undefined='

Important. You can only introspect tokens that were issued to a confidential client; you cannot inspect tokens issued by a public client. Why not? Well, to introspect a token you must employ Basic authentication, with the OIDC client ID used as the username and the OIDC client secret used as the password. That’s no problem with confidential clients. However, public clients don’t have client secrets. As a result, there’s no password that you can use with the introspection endpoint.

After you issue that command, the introspection endpoint sends back token properties and property values similar to these:


{
     "active": true,
     "scope": "address email openid phone profile",
     "client_id": "a39796ab-75tg-po9f-3aa5-7yh22kj03a3",
     "token_type": "Bearer",
     "exp": 1552603442,
     "iat": 1552599842,
     "sub": "2edd2f32-1e49-4bf2-b164-763781761b52",
     "aud": [
        "a39796ab-75tg-po9f-3aa5-7yh22kj03a3",
        "https://documentation.akamai.com"
     ]
 }

These properties are explained in more detail below.

active

Returns true if the access token is valid, and returns false if the access token has expired. If the token has expired then only the active value is returned:

{
    "active": false
}

No other token properties will be available. This is a security measure that helps prevent malicious actors from using expired tokens to learn the details of how your tokens are constructed. 

scope

Specifies the user profile information that the token provides access to. For example, the phone scope provides access to the phone_number and phone_number_verified claims (attributes). See the article Scopes and Claims for more information.

client_id

Unique identifier of the OIDC client that requested the token. An OIDC client is any entity  that can connect to an OIDC server and request user authentication. 

token_type

Specifies the type of token issued. A Bearer token indicates that access and permissions should be granted to whoever has possession of the token: no attempt is made to verify that the token really belongs to the person who possesses it. You can think of bearer tokens as being similar to cash. If you pay for a purchase by writing a check or by using as credit card. you will likely be asked to provide proof of your identity. If you pay with cash, however, you don’t need to prove that the cash belongs to you: possession of the money is all you need.

Incidentally, the fact that access tokens are bearer tokens is one reason why access tokens have relatively short lifespans: should an access token somehow “leak out,” anyone who gained possession of that token would have only a limited amount of time in which they could use it.

Note. We should also add that, as long as you use TLS, it’s very difficult for a malicious user to steal an access token. OAuth and OIDC are extremely secure, provided that you follow security best practices. 

exp

Specifies the date and time when the token expires. The exp claim is formatted using Unix epoch time, which represents the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC) on January 1, 1970. For example, the value 1553405263 represents Saturday, March 23, 2019 at 22:27:43 Pacific Daylight Time. See Converting Unix Epoch Time for more information.

By default, access tokens expire 1 hour after they are issued, while refresh tokens expire after 90 days.

iat

Specifies the date and time when the token was issued. The iat (issued at time) claim is formatted using Unix epoch time, which represents the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC) on January 1, 1970. For example, the value 1553405263 represents Saturday, March 23, 2019 at 22:27:43 Pacific Daylight Time. See Converting Unix Epoch Time for more information.

sub

Unique identifier for the end user (the subject). This will always be the user’s Akamai Identity Cloud UUID (Universally Unique Identifier).

aud

Audience that the token is intended for. For Hosted Login, you will always see both the client ID (e.g., a22c9604-7b27-464f-bff5-83ba229323af) and the redirect URI (e.g.,  https://documentation.akamai.com) included in the aud claim.

 

Identity Tokens

Identity tokens are an example of a JSON web token (often abbreviated as JWT, and usually pronounced “jot”). A JSON web token is made up of three sections, with each section in the token separated by a period. For example, a JWT token might look similar to this, with different colors used to highlight the three sections:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkpzcWY1ZzVfR0s2Z3Y5Mk1HZFcxcDZUbE5RM25sTXZRM3hEeTFXZ
WQ0MlEifQ.
eyJzdWIiOiJjM2Q2NzExOC1iNWI3LTQ3YzMtOGYyNi1iOGEyZDQwMzhjNDciLCJpYXQiOjE1MzA4MjgxNzQsIm
V4cCI6MTUzMDgzMTc3NCwiYXVkIjoicHJpdmF0ZWdyb3VwcyIsImlzcyI6Imh0dHBzOi8vYXMtZXVkZW1vOXgwcHVrdC5k
ZW1vLm1pYWFndWFyZC5jb20iLCJzY3AiOlsib3BlbmlkIiwicmVhZF9ob21lIiwibWFuYWdlX2hvbWUiXSwiY2lkIjoiMjZyd2h
td3J0bWo4Zm1kdDg3cnc4NXRkNmZtamo0ZWUiLCJqdGkiOiJPRE5qTWpJeE5tSXRZamszWXkwME9EYzFMV0V4TlRjdE5X
TTRPR0ppWXpka1lUVXkifQ
.PpqrdT1b2q01EBQ1lByxKXx4JAVcuAE4Rq1gx3LE2yU

The three sections of a Hosted Login identity token include:

  • The header (shown in red)
  • The payload (shown in blue)
  • The signature (shown in green)

These three sections are discussed in more detail below. 

Identity Token Properties: Header

The header is used to describe the hashing algorithm (for example, RS256), the token type (always JWT, for JSON Web Token), and, optionally, the key identifier (kid). Key identifiers specify the JSON Web Key used to sign the token, and are usually omitted unless you use multiple keys for signing tokens.

A header section looks similar to this:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "Jsqf5g5_GK6gv92MGdW1p6TlNQ3nlMvQ3xDy1Wed42Q"
}

These properties are described below:

alg

Identifies the cryptographic algorithm used to sign the token. For Hosted Login, this value will always be RS256, which references the hashing algorithm RSASSA-PKCS1-v1_5 using SHA-256.

kid

Key identifier, a case-sensitive string that indicates the jJSON Web Key used to sign the token. Each JSON Web Key includes a kid property that corresponds to the kid property used in the token header. 

kid is an optional property that is typically present only if you use more than one signing key.

typ

Specifies the type of token being returned. For an identity token, this value will always be JWT.

 

Identity Token Properties: Payload

The payload contains the actual information being transmitted by the token, as well as any additional metadata about the user or the token that might be needed. Payloads are made up of a series of claims, with a claim simply referring to a name/value pair (like “name”: “bob”or “color”: “red”). For example:

{
  "iss": "accounts.janrain.com",
  "sub": "8855454e-8146-11e8-adc0-fa7ae01bbebc",
  "aud": "c2a5b7bc-e329-b4e4-dd6b-eb1ae01c22aa",
  "iat": 1530897246,
  "exp": 1530900246,
  "jti": "ID.rWH0iZkhFNxAoDxR5LhLAOqNj2bQvmMaeQiqhH5BcAU"
}

Hosted Login payload claims are described below. Before we go much further, however, we should mention that payloads are encoded (using base64url encoding) but they are not encrypted. Because of that, you should avoid including confidential information (such as a user password) in a payload whenever possible.

Note. If payloads aren’t encrypted, then why bother encoding them? That's mainly done to ensure that all JWT tokens use the same character encoding, deftly sidestepping the issues that could arise if one system in the authentication chain uses UTF-8 encoding while another uses ISO 8859-1 encoding.

at_hash

Access token hash value, used to verify which access token was issued along with the identity token. The at_hash value is calculated by:

  1. Using the token’s hashing algorithm (alg) to hash the access token.
  2. Base64url encoding the leftmost half of the hash value octets.

aud

Audience that the token is intended for. For Hosted Login, you will always see both the OIDC client ID (e.g., a22c9604-7b27-464f-bff5-83ba229323af) and the redirect URI (e.g.,  https://documentation.akamai.com) included in the aud claim.

auth_time

Indicates the last time that the user was authenticated. The auth_time claim is formatted using Unix epoch time, which represents the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC) on January 1, 1970 For example, the value 1553405263 represents Saturday, March 23, 2019 at 22:27:43 Pacific Daylight Time. See Converting Unix Epoch Time for more information.

azp

Specifies the authorized party to which the token was issued. For Hosted Login, the authorized party will always be the OIDC client ID.

exp

Specifies the date and time when the token expires. The exp claim is formatted using Unix epoch time, which represents the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC) on January 1, 1970. For example, the value 1553405263 represents Saturday, March 23, 2019 at 22:27:43 Pacific Daylight Time. See Converting Unix Epoch Time for more information.

By default, identity tokens expire 1 hour after they are issued.

global_sub

Contains all the information needed to locate a specific user. For example:

capture-v1://capture-alb-border.us.janrain.com/x3gmnnjeyzyrrt2nm5drf5nkn8/user/2edd2f32-1e49-4bf2-b164-763781761b52

In the preceding example:

  • capture-v1://capture-alb-border.us.janrain.com– Internal path to the Hosted Login identity store.
  • x3gmnnjeyzyrrt2nm5drf5nkn8– The Hosted Login application ID.
  • user– The entity type where user profiles are stored.
  • 2edd2f32-1e49-4bf2-b164-763781761b52– The Hosted Login UUID (sub) of the user.

iat

Specifies the date and time when the token was issued. The iat (issued at time) claim is formatted using Unix epoch time, which represents the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC) on January 1, 1970. For example, the value 1553405263 represents Saturday, March 23, 2019 at 22:27:43 Pacific Daylight Time. See Converting Unix Epoch Time for more information.

iss

URL for the endpoint that issued the token.

sub

Unique identifier for the end user (the subject). This will always be the user’s Akamai Identity Cloud UUID (Universally Unique Identifier).

Identity Token Properties: Signature

If you buy a house or a car, you need to sign a document (or a set of documents): that signature serves as proof of your identity, and attests that it really was you who bought the house or the car. The signature in a JWT token serves a similar purpose: if the signature can be verified, it proves that the JWT has not been changed since the token was issued AND that the token was issued by Hosted Login.

To create a signature for a token you need to:

  1. Combine the header, the payload, and a secret (a private key maintained by Hosted Login).
  2. Encrypt the resulting string by using one of the allowed encrypting algorithms. The most commonly-used signature algorithm is HMAC SHA256 (aka HS256).

The fact that the header and the payload are part of the signature explains how the signature helps verify that the token contents have not been changed since the time token was issued. For example, suppose we have a sample token issued to the user Bob Jones. We then change the audience in for that token to Robert Jones instead of Bob Jones. That's a simple enough change, but notice what happens to the token:

Token for Bob Jones

Token for Robert Jones

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJCb2Ig
Sm9uZXMiLCJpc3MiOiJBa2FtYWkgSWRlbnRpdHkgQ2xvdWQ
iLCJleHAiOjE1NTUxOTY4NDYsImlhdCI6MTU1MzAxMTgyNX0

.i2_tN25AxQqYvvD-KeKSQdXLNd1V9t3AWDoelZDCAXM

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJSb2
JlcnQgSm9uZXMiLCJpc3MiOiJBa2FtYWkgSWRlbnRpdHkgQ
2xvdWQiLCJleHAiOjE1NTUxOTY4NDYsImlhdCI6MTU1MzA
xMjE1MH0
.e38aMWhGIEjDPhKLhGfhG7KYTyoZ-QJkoRKJDZ0170

As you can see, the header is the same in each token. And that makes sense: after all, the header is the same in each token. The payload is different, however, because the two payloads are different. (The difference might be minor, but it's a difference nonetheless.) And because the payloads are different, that means that the signatures should also be different. And they are. 

Anti-forgery State Token (Nonce)

Cross-site request forgery attacks occur when a user is tricked into running an unauthorized command while logged on to an app or website. (Typically this happens when a malicious link is embedded into the app of website.) One way to help guard against CSRF attacks is to use an anti-forgery state token when making authentication requests. This token is included in the initial authorization request as the value assigned to the state parameter and is returned by the authorization server along with the authorization code. The client can then compare the original value of the state parameter with the returned value and verify that they are the same. If they aren't, that suggests that some sort of CSRF attack might have occurred.

Here's what the state parameter looks like in an API call:

https://v1.api.us.janrain.com/00000000-0000-3000-8000-000000000000/login/authorize?
client_id=kbyuFDidLLm280LIwVFiazOqjO3ty8KH
&redirect_uri= https://documentation.akamai.com/callback
&scope=openid profile email phone address
&response_type=code
&state=b49135acc48faea8fa0b641456523967f766ccb4

Anti-forgery state tokens differ from other OIDC tokens in that they are not generated by the server; instead, you create the token yourself and include it in your authentication request. Although the token value can be any character string of your choice, it's recommended that you use a string of at least 32 characters generated by a random number generator. The resulting value is then used to configure the state parameter:

state = 99846266547289293014765635352342

So how do you generate a 32-character string by using a random number generator? One easy way is to use an openssl command like this:

openssl rand -hex 16

In turn, that gives you back something similar to this:

f5e32a98c0b526f317a87cfd12ebd955

Note that the state parameter is not required. Instead, it’s simply an option available to you if you want to use it.

Trivia. You will often hear the anti-forgery state token referred to as the “nonce.” Nonce is an English word referring to a word or a term coined for a specific occurrence or occasion.