An introduction to OAuth 2.0 and OpenID Connect

Some things were just meant to be together. Peanut butter and jelly. Abbott and Costello. Alien and Predator. And, of course, the match-made-in-heaven that we’ve all been waiting for: the Akamai Identity Cloud and OpenID Connect.

Now, to be fair, the Identity Cloud and OpenID Connect (more-commonly referred to as OIDC) aren’t exactly strangers; in fact, the Identity Cloud’s predecessor (Janrain) was one of eight OpenID Sustaining Corporate Members:



However, while Janrain had long been involved with the OpenID Foundation and was a long-time supporter of OpenID Connect, OIDC was always a somewhat-peripheral part of the company’s customer identity and access management (CIAM) platform. But now, with the release of Hosted Login, OIDC has taken center stage. And that can only mean one thing: it’s time to formally introduce Akamai Identity Cloud subscribers to OpenID Connect.

Before we go any further, we should clarify that this documentation is exactly what it purports to be: an introduction to OpenID Connect. Why did we feel the need to put together an introductory article like this? Well, as it turns out, OIDC is one of those things that many people have heard of but few people truly understand. Because of that, we thought it might be useful to offer a primer on OpenID Connect and how it works in general. Are you already familiar with things like anti-forgery state tokens, authorization flows, payloads, and nonces? In that case, this article might not be for you, and you have our permission to stop reading. But if you’re not completely sure what the difference is between an identity token and access token, well, then you might want to keep going, at least for a little while.

📘

We should also clarify that this article pertains only to the Akamai Identity Cloud's Hosted Login solution. As of this writing, other uses for OAuth and OpenID Connect (such as the ability to integrate with third-party OIDC providers) is not supported.

OpenID Connect and OAuth 2.0

By definition, OpenID Connect is an "identity layer built on top of OAuth2." That’s a good answer, but it does leaves us with one question: what does it mean to be an "identity layer built on top of OAuth2." Let’s see if we can explain that a little better.

To begin with, being an "identity layer" simply means that OIDC is employed in user authentication and authorization. At its core, OIDC is a secure mechanism that enables an app or a website to do the following:

  1. Contact an identity service provider (like the Akamai Identity Cloud) and authenticate the user.

  2. Receive an access token that (in the case of Hosted Login) specifies the user profile information that the client is authorized to access.

  3. Securely retrieve personal information (such as information extracted from a user profile) needed by the user or by the system.

  4. Periodically refresh the user’s authentication/authorization state, and prevent that session from timing out. By default, access tokens (which act as a sort of admission ticket to a protected resources) have a very limited lifetime, a lifetime usually measured in minutes as opposed to hours or days. Consequently, these tokens need to be refreshed on a regular basis. If they aren't, you could find yourself being unceremoniously blocked from resources even though your session just started. Needless to say, that’s not a very good user experience.

That, in a nutshell, is OIDC. So then how does OAuth 2.0 fit into the picture? Well, like the definition said, OIDC is built on top of OAuth 2.0: OAuth 2.0 is framework that enables clients (apps or websites) to obtain access to resources controlled by the user (for Hosted Login, that’s the user’s user profile). As a framework, OAuth 2.0 establishes a standard set of authentication/authorization message flows using JSON (JavaScript Object Notation) and HTTPS; however, OAuth doesn’t dictate the process by which clients use these message flows. To speak metaphorically, OAuth built the highway, but it doesn’t dictate how you actually use that highway. Want to drive up the coast using a car or a truck or a motorcycle? That’s up to you. Or, in this case, up to you and OIDC.

For you history buffs out there, the Internet Engineering Task Force (IETF) published the OAuth 2.0 framework in 2012 in a pair of RFCs (RFCs 6749 and 6750). Today, OAuth 2.0 and its extensions are covered in 9 different RFCs, meaning that several hundred pages of documentation are needed to detail the technology. Needless to say, that's one big, long standard.

Which brings up an obvious question: do we even need a big, long standard that spells out how apps and websites can provide users with access to resources? Can’t each app and each website implement their own authentication/authorization protocol and call it good?

Well, yes, they can, and, for many years that's exactly what they did. As it turns out, however, there are some major advantages to following a standard authentication and authorization protocol, beginning with the obvious: with a standard in place, websites and apps don’t need to invent, and implement, their own processes and protocols. In turn, that means that:

  • The authentication process can be handled by an identity service provider, freeing the website/app developers to focus on other activities.

  • Apps and websites no longer have to manage password files (and no longer have to safeguard those password files).

Those two factors alone argue strongly in favor of standardized authentication.

But that’s not all. In addition to freeing up developer time and removing the burden of password management, using a standardized, web-based identity provider means that:

  • Authentication can be shared across multiple apps and multiple websites. For example, suppose your organization markets several different fruit juices. A user could log on to the orange juice site and automatically be authenticated for the grape juice, apple juice, and cranberry juice sites as well. And that's a good thing: no one wants to try and remember usernames and passwords for orange juice, apple juice, and cranberry juice.

  • Authentication can evolve without requiring app changes. Suppose you want to make changes to the way you carry out authentication/authorization. If each app and each website has its own authentication protocol, you must make your changes in each location. With a web-based identity provider, changes only need to be made at the identity provider itself. Why? You got it: because that's the only place where authentication and authorization take place.

    In other words, a web-based identity provider helps reduce the complexity of client applications and websites. Clients can be kept simple, with the more-complicated mechanisms relegated to the server. (And, as an added bonus, a server that someone else manages for you.)

  • Apps never know your username and password. This might seem counter-intuitive, but, with OAuth, apps and websites never see your password. This provides a robust measure of security: anyone intent on stealing your credentials will have to do so by hacking the identity provider rather than by hacking your app or your website. As a general rule, identity providers are more tightly locked-down than your typical app or website.

    If this seems a little confusing, maybe this will help. The tried (if not always true) method for authenticating users was to have the client impersonate the user: the user would give his or her credentials to the client, and the client would then “replay” those credentials at logon time, essentially telling the server, “No, really, I am the user.”

    With OAuth and OpenID Connect, however, the client never sees the user’s credentials and the client does not pretend to be the user. Instead, the client contacts the server and says, “Hi. Bob says I can do X, Y, and Z for him. Is that OK?” The user then authenticates with the server, and the server tells the client, “I just checked with Bob, and he says yes, you can do X, Y, and Z on his behalf.”

    The two key differences here? First, the client has been delegated permissions by the user, but the client is not impersonating the user; instead, the client is simply working on the user’s behalf (and with his or her permissions). Second, the client doesn’t necessarily have all the permissions that the user does. In this example, the client has been granted the right to do X, Y, and Z; the fact that the user can also do A, B, C, and D is irrelevant.

📘

Handing a client your credentials and having that client impersonate you is often referred to as the authentication “antipattern.” An antipattern is an all-too-common response to a recurring problem, albeit a response that is typically ineffective and often highly counter-productive. For example, because complex passwords are difficult to remember, people often write those passwords on a sticky note and leave that note stuck to their computer or monitor. That’s an antipattern.

That's the value of OAuth, which is pretty valuable in and of itself. On top of that, however, OpenID Connect adds a number of incredibly useful features. For example, OpenID Connect:

  • Is device-agnostic. Logging on with a desktop computer? Logging on from a cell phone or tablet? It doesn't matter. OIDC employs the same user experience regardless of your device type. You don’t have to set up separate authentication procedures for Android phones and for iOS phones and for Windows computers and for iPads and …. Instead, one size fits all.

  • Adds the identity token. OAuth is based on tokens (most notably access and refresh tokens), but most of these are bearer tokens: these tokens can be used by anyone who happens to be in possession of them (in the OAuth world, possession is ten-tenths of the law). So how do you know that the user in question is the legitimate owner of that token? Well, you don’t.

    That’s where identity tokens come in. Identity tokens are like the deed to a house or the title to a car: they prove who is the rightful owner of the access token. And prove is not too strong a term: that’s because identity tokens are signed and encrypted. In theory, a hacker could steal an identity token and then change some of the token information. As soon as they change anything, however, the signature – and thus the token itself – becomes invalid. If you have an identity token that says you’re the owner of a given access token, well, that’s hard to argue with.

In other words, that's what OAuth and OIDC are and why they’re important. But how do they actually do those things? To understand that, you’ll need to learn how to speak OIDC.

How to speak OAuth and OIDC

Like most technologies, OAuth and OIDC have their own terminology, a vocabulary – claims! nonces! payloads! authorization codes! – that can seem overwhelming at first. (Although, to be fair, is that any worse than, say, cricket, a sport that features such terms as death bowler, cow corner, and dibbly doobly?) Fortunately, most of these "scary" terms represent fairly simple concepts. If you can master the OAuth/OIDC terminology, then you've gone a long ways towards understanding what these technologies do and how they work.

And how are you supposed to master this OIDC terminology? Funny you should ask ….

Who’s who in OAuth and OIDC

Both OAuth and OIDC revolve around roles and role-playing: you have resource owners and clients and relying partners and – well, you get the idea. Here’s a quick overview of the key players in an OAuth/OIDC implementation:

  • Resource owner. Technically speaking, a resource owner is “any entity capable of delegating access to a protected resource.” In human-speak, and for our purposes, what we’re really talking about is an end user. The end user (i.e., the resource owner) effectively tells the OIDC client, “You have my permission to connect to this website and do the following things on my behalf.” As we noted earlier, this is a twist on traditional authentication/authorization methods: with OAuth and OIDC the client does not impersonate the resource owner, but works on behalf of the resource owner.

    And yes, we know: the term “resource owner” can be a little confusing, especially because the resource owner is asking permission to access a resource, something that he or she, based on the names alone, would appear to own. But, remember, the resource owner isn’t asking for permission to the resource; he or she already has that permission. Instead, the resource is asking for permission for the client to access the resource on behalf of the owner.

    It’s definitely a little confusing, which is why we will typically use the term user or end user in our documentation.

  • Client. A third-party application that requests access to the private resources of the resource owner. An OAuth/OIDC client does not pretend to be you; instead, it simply tells the server, “Hi. Bob has authorized me to do the following things for him.” If approved, the client will only be able to do those things that the resource owner has authorized it to do: for example, if the client has been authorized to return the email scope only, then that client cannot return the profile and address scopes as well.

    Hosted Login supports two kinds of OIDC clients: confidential and public. Confidential clients have a client secret, and public clients do not. Does that matter? Yes, it does. Because confidential clients require the use of a client secret (i.e., client password) this type of client is not recommended for mobile apps. By comparison, public clients don’t use a client secret (instead, they use PKCE: Proof Code for Key Exchange ) to help verify the authentication process. Because they don’t use client secrets, public clients should be used by mobile devices and by any other client that can’t be trusted to protect a client secret.

    Incidentally, OIDC clients are also known as RPs or “relying parties". That’s because they rely on an outside service (the authorization server)) to vouch for the identity of a user: an RP requests authentication for a user, but does not actually perform the authentication itself. The authorization server, in turn, is also referred to as the OP (OpenID Provider).

    And while it's easy to do, try not to confuse OIDC clients with Hosted Login API clients. OIDC clients are a key part of the OpenID Connect specifications, and a key part of the actual authentication mechanism (for example, OIDC clients help direct you to the login URL). By comparison, API clients, which have long been part of the Akamai Identity Cloud, help determine the look and feel of the login/registration experience. This is because the API client’s provide access to the Hosted Login flow.

    One quick way to tell the different clients apart? Look at the client ID. OIDC clients will have a ID that looks like (and is) a UUID:

    20aaae77-d471-4005-a8b2-dbeb4e524577

    API clients have an ID that looks like this:

    mhxvbnu97ru492weg698ue388jmdm4jw

    In addition, the OIDC client is specified in your authentication request

https://v1.api.us.janrain.com/00000000-0000-3000-8000-000000000000/login/authorize?
    client_id=a22c9604-7b27-464f-bff5-83ba229323af
    &redirect_uri=https://documentation.akamai.com/callback
    &scope=openid profile email
    &response_type=code
    &state=3bd5262737237ef4a
The API client does not appear in any of the OIDC requests or responses. Instead, the authorization server looks up the API client ID based on the client_id parameter used in the authentication request.
  • *Protected resource.: Any resource that can only be accessed by using an authenticated client; often-times this is the only reason you might require users to log on in the first place. (If all sections of your app/website are available to all users there might be no need to require users to have accounts and to sign in.) More often than not, protected resources are APIs: for example, you might have an API for creating accounts that is only available to users who have the “Create new accounts” permission.

    In Hosted Login, the userinfo endpoint (responsible for viewing user information) is the only protected resource: you must have a valid access token before you can retrieve or modify user profile data.

  • Authorization server. As the name implies, the OAuth endpoint that authorizes users. In OpenID Connect, you will see the authorization server referred to as the OP (i.e., the OpenID Provider).

Authorization flows and grants

In OAuth, a "flow" describes the allowed authentication and authorization scenarios. Although OAuth supports several different flow types, the most-commonly used grant type is Authorization code + PKCE grant type. PKCE (pronounced “pixie”) is an authentication method that uses “code challengers” as a way to verify the transactions between the client and the token exchange endpoint. Using code challengers helps secure the authentication process without having to exchange a client secret. This is especially important on mobile devices, where best practice strongly discourages the use of client secrets.

Other OAuth flow types include:

  • Implicit. With the implicit flow, the client contacts the authorization server directly, without going through a middleware client such as the Identity Cloud’s JavaScript SDK (widget). This flow type is being deprecated in favor of Authorization Code + PKCE.

  • Refresh token. The authorization endpoint directly issues clients an access token and/or an identity token, and does so without requiring the client to visit the token endpoint.

  • Hybrid. With this flow, after each successful authentication the server returns something other than an authorization code. (Typically, this is an access token).

  • Client credentials. Authentication method that involves no user interaction. Oft-times, this flow is reserved for backend operations such as data integrations. In Hosted Login, the grant enables you to obtain the configuration access tokens required to call the OIDC Configuration APIs.

And yes, the term flow is used elsewhere in Hosted Login to refer to something completely different (that flow is a JSON file containing configuration information for managing user logins and registrations). Because of that, you might want to use the alternate term authorization grant when referring to OAuth flows. But that's up to you.

Tokens

Tokens are small pieces of digital information (typically formatted as a JSON object) that are used for authentication and authorization purposes.

When working with OIDC, you will typically encounter four different token types:

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

Identity tokens

As the names implies, the identity token verifies your identity: like a passport, it states that you are exactly who you say you are. Identity tokens:

  • Assert the user's ID
  • Indicate the issuing authority (e.g., Akamai)
  • Can optionally specify how, and when, the user was authenticated
  • Are generated for a particular audience (client)
  • Include an issue date and an expiration date (which helps server determine token validity)
  • Can optionally contain user profile details such as name and email address
  • Are digitally signed so that the token can be verified by the intended client

Identity tokens are typically not used for authorization. Instead, identity tokens are intended to be used by the OpenID Connect library (client) that made the authorization request; the uses of an identity token range from helping to verify the legitimacy of the access token (the access token you received must match the access token specified in the identity token) or for personalizing the user experience. Upon receipt of an identity token the client should:

  • Verify that the token is correctly formatted.
  • Verify the token signature.
  • Verify that token validity by checking the exp claim (to ensure that the token has not expired).
  • Verify that token was addressed to the receiving party (by checking the aud claim).
  • Verify that the token issue (the iss claim) matches the issuer specified in the discovery document.

Access tokens

To repeat what we said a moment ago, identity tokens let everyone know who you are. Meanwhile, access tokens let applications and websites know what you're allowed to do. If you request access to a resource, the server is going to check the access token and make sure you're actually allowed access to that resource.

Actually, there's an important clarification to make here: the server will check to see that the token is allowed access to the resource. As we mentioned earlier, access tokens are "bearer tokens," which means they provide access to anyone who has possession of (i.e., who bears) that token. No ID checks are performed when you access a resource; instead, possession of the token is all that matters.

As you might expect, that’s also why access tokens are typically short-lived: if a token does leak out, there’s only a limited amount of time in which the token can be used for malicious purposes.

Refresh tokens

Access tokens typically have a short lifetime: at best, the access token expires after 1 hour. (And what happens then? If your access token expires you won’t be able to access protected resources until you obtain a new access token.)

That’s where refresh tokens come in. Refresh tokens provide a way for clients to stay logged on to a web site or application for – well, for however long you want to allow users to stay logged on for. For example, by default Hosted Login access tokens have a lifespan of just one hour. By comparison, refresh tokens have a default lifespan of 90 days. Suppose you log on to a website at noon. At 1:00 PM your access token is ready to expire. Does that mean you have to reauthenticate? Well, depending on your application logic, it would if you didn’t have a refresh token. Right before the access token expires, you can use the refresh token to exchange the old access token for a new one; that gives you another hour to continue your session. At 2:00 PM your second access token will be ready to expire. Once again, however, you can use the refresh token to exchange the old access token for another new token. This can continue until you log out or until your refresh token expires (some 90 days from now).

Anti-forgery state token

Anti-forgery state tokens differ from other OIDC tokens in that they are not generated by the server; instead, the client creates the token and includes it in the authorization 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 what’s the point of all that? Well, when you make your request, you include the anti-forgery state token as a part of that request. When the authorization response is returned, that response must include that same anti-forgery state token. If the two tokens match, that makes it very likely that the authorization response is legitimate. If they don’t match, well ….

Allowed values in an anti-forgery state token include:

  • Alphanumeric values (both uppercase and lowercase)
  • The underscore (_)
  • The dash (-)
  • Blank spaces and other special characters (., #, * etc.) are not allowed.

Scopes and Claims

In English, an aptronym is a name that aptly describes a person; for example, Tommy Tune seems like the perfect name for a singer, dancer, and choreographer. We bring this up for one reason and one reason only: in the OIDC world, scopes and claims probably don't quality as aptronyms.

Names aside, however, scopes and claims are actually fairly easy to understand: both terms apply (at least in Hosted Login) to the personal information that a client is requesting from the server. With that in mind, let’s start with the scope, a collection of predefined user information sets. OIDC defines several scopes, including:

  • openid. Because this scope identifies your request as an OIDC request, this scope must be included in all your authentication requests.

  • profile. Requests access to the following claims: name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.

  • email. Requests access to the email and email_verified claims.

  • address. Requests access to the user's mailing address.

  • phone. Requests access to the phone_number and phone_number_verified claims.

📘

For information on how these OIDC claims map to Identity Cloud user profile attributes, see the article Scopes and claims.

You can include multiple scopes in your API calls by separating the scopes with a blank-space. For example:

scope=openid email phone

In other words, scopes are just collections of claims. And what are claims then? Claims simply provide a way for you to return information about a specific user profile attribute or set of attributes.

Information about the default claims and scopes supported by a given authorization server can be found in the well-known endpoint (see below).

Endpoints

In OIDC, the word endpoint refers to a server connection (typically a URL) that plays a role in the OpenID process. The most common OIDC endpoints include the following:

  • Well-known endpoint. Also referred to as the "discovery document," the well-known endpoint is set of OIDC values that can be retrieved by a client; in turn, this enables clients to configure themselves. For example, you should never have to specify the available scopes for a client. Instead, your client can simply connect to the well-known endpoint and retrieve information about the allowed scopes.

    The well-known endpoint can be reached by adding /.well-known/openid-configuration to the end of your domain’s issuer URI. For example, if your issuer URI is https://documentation.akamai.com/login then your well-known endpoint will be found here (note that this is not a working URL):

    https://documentation.akamai.<span>com/login/.well-known/openid-configuration</span>

    And what exactly will you find at that well-known endpoint? Here's one example; it's a list of all the different grant types that the server supports:

    "grant_types_supported": [
       "authorization_code",
       "refresh_token"
    ],
    
  • Authorization endpoint. Validates the authorization request and redirects the user to the Hosted Login login page.

  • Token endpoint. After a client has received an authorization code, that code can be presented to the token endpoint. In exchange for that code, then token endpoint gives the client an identity token, a refresh token, and an access token. The token endpoint also handles all requests to refresh access tokens.

  • Userinfo endpoint. Returns the requested user profile information to the client. Note that the userinfo endpoint returns only the information that has been requested. To retrieve information from this endpoint, your request must include a valid access token.

  • Introspection endpoint. Provides a way to decipher (and thus view the content of) an access token or refresh token. Introspection endpoints are often used by resource servers whenever a client requests access to a resource: the resource server forwards the token to the introspection endpoint, and the introspection endpoint reports whether or not the token is still active and, if so, what permissions that token allows.

    As noted, the introspection endpoint can only be used with access tokens and refresh tokens. If you want to examine the contents of an identity token, you’ll have to use a different process. For example, most OIDC libraries are capable of introspecting an identity token.

  • Revocation endpoint. Provides a way for an access token or a refresh token to be revoked. Often-times, tokens are revoked by a client after a user logs out: the client calls the revocation endpoint and has the token invalidated. That prevents a valid token from “leaking out” and being snatched up (and then used) by a malicious actor. However, an administrator can also revoke a token at any time by calling the /login/token/revoke endpoint and passing the token as the request body. One advantage to this? In case of a suspected security breach, it provides a way to prevent a user from accessing resources without forcing that user to change his or her password.

📘

When a token is revoked, the user is not ejected from the website or app. However, without a valid token the user cannot access protected resources until he or she has reauthenticated.