Authorization Code for Web Apps

As noted elsewhere, what makes Hosted Login so revolutionary aren’t the mechanics involved in doing such things as logging on or registering for an account; after all, most of your users will still log on by providing a user name and a password (and most of the rest will log on socially by first logging on to an identity provider such as Facebook or Twitter). To be honest, one of that is the least bit revolutionary. Instead, what makes Hosted Login so ground-breaking are all the things that you don’t see, starting with the fact that – under the covers – Hosted Login is powered by OAuth 2.0 and OpenID Connect (OIDC).

But even though you don’t really ”see” OpenID Connect that doesn’t mean it isn’t important; after all, you typically don’t see oxygen and oxygen is pretty important to most of us. In fact, if you hope to truly understand what Hosted Login is and how it works, you need to understand what it means to authenticate by using OIDC.

Now, admittedly, that’s easier said than done: after all, OIDC implementations can vary, and there is no single, prescribed way to do OIDC authentication. However, the authentication method employed by Hosted Login uses the same basic approach used in most OIDC scenarios. If you can master that approach, you’ll have a good grasp of what it means to authenticate by using OpenID Connect. And, needless to say, you’ll have a very good grasp of what it means to authenticate by using Hosted Login.
Note: You say you aren’t even sure what Hosted Login is? In that case, you might want to start by reading the article An Introduction to Hosted Login.

How OIDC (and Hosted Login) Work

So you want to know how OIDC (and, by extension, Hosted Login) works? Let’s start by providing a brief overview, then looking at each step in more detail. Here’s that brief overview.

  1. You launch your app or visit your website and click Login. In response, an authentication request is sent to the authorization endpoint. Note your request must include such things as the client ID for the OpenID client being used to logon; a redirect URI specifying where the user should be redirected after a successful logon; and an anti-forgery state token that helps ensure the validity of the token exchange process.
  2. The authorization endpoint returns a login page to the app or web browser. This page includes any consents that you must agree to before you can log on or before any personal data can be returned.
  3. The users attempts to log on at the authorization endpoint.
  4. If authentication succeeds, a redirect URI is sent back to the app or web browser: embedded within this URI is your authorization code. At the same time, the anti-forgery state token is verified: authentication continues only if the anti-forgery state token is deemed valid.
  5. Your app or web browser sends the authorization code to the token endpoint, and exchanges that code for an access token, an identity token, and a refresh token.
  6. As soon as you have your tokens, you can begin accessing protected resources such as your user profile.

What follows is a more detailed look at the information exchanged during the Hosted Login authentication process, along with a discussion of how this process is carried out.

The Initial Authentication Request

The authentication process is typically kicked off when a user visits your website or opens your app and clicks Login. Kicking off the authentication process takes the user to your authorization endpoint and to your login page. For example, the user might be redirected to a URL such as this:
https://v1.api.us.janrain.com/00000000-0000-0000-0000-000000000000/login/authorize

But there’s more to an authentication request than simply pointing the user to another web page. Instead, that request must include at least some of the parameters included in the following table:

Parameter Required Description
client_id Yes Unique identifier of the OIDC Client making the request. For example:
22c9604-7b27-464f-bff5-83ba229323af
response_type Yes Specifies the authentication and authorization flow type. For Hosted Login, the response_type must be set to code, which means that, after a successful authentication, the user is sent an authorization code. That code must then be exchanged for an access token before the user can access resources.
scope Yes
Indicates the scopes you are requesting access to. For example:
openid profile email

Scopes are collections of claims, and claims (for the most part) map to individual user profile attributes. For example, the email scope consists of two claims: emailAddress and emailVerified. With OpenID Connect, the openid scope must always be requested. This tells the authorization server that you want to authenticate by using OIDC.

redirect_url Yes
The URI (e.g., the web page) that clients are redirected to after being authenticated. For example:
https://documentation.akamai.com

As you make your way through the authentication process, you’ll see that the redirect URI appears in several different API requests and API responses. The redirect URI must be the same in every request/response or the API call will fail.

state Yes
Value of the anti-forgery state token. For example:
99846266547289293014765635352342

The anti-forgery state token (also referred to as the nonce) is a randomly-generated value included in an authorization request and then returned along with the authorization code. The client can check the value returned from the authorization server with the value of the state parameter from the original request. If the values match, that helps verify that the authorization code is legitimate. If they don’t that suggests that something has gone wrong with the authentication process. In that case, authentication should be canceled and then restarted.

prompt No

Specifies the system behavior when a user needs to be reauthenticated (for example, because the max_age limit has been exceeded). Hosted Login supports two prompt values:

  • none. When a user needs to be reauthenticated, the authorization server first checks to see if the user currently has a valid Hosted Login session. If true, the user is then “silently” reauthenticated (that is, no authentication dialog box is displayed, and the user never knows he or she was reauthenticated). If false, an error is returned, and the user must reauthenticate in order for their session to continue.

  • login. The user is always asked to reauthenticate, regardless of whether he or not he or she currently has a valid Hosted Login session.
max_age No Specifies the maximum amount of time (in seconds) that a logon session can last before the user is required to reauthenticate.
id_token_hint No

Specifies that a token previously issued by the authorization server should be passed along with the authentication request. If the end user associated with the token is already logged on, then no new authentication is required and the user remains logged on. If the end user is not logged on, then he or she will be required to sign in.

If you decide to use the id_token_hint parameter you should also include the prompt=none parameter in your request.

ui_locales No
Specifies the end user’s preferred language (or languages) for the login and registration user interfaces. Language preferences should be passed as a set of space-delimited RFC5646 language codes; for example:
"en-US en-GB fr-CA"

In the preceding example, Hosted Login will first attempt to render the UI in US English. If that fails, the UI will be rendered in British English and then, if necessary, Canadian French.

If this parameter is not present, the default language and local will be used. Note that no error will be returned if you specify an invalid language code, or if your application does not support a specified language.

An actual authentication request looks something like this:
https://v1.api.us.janrain.com/00000000-0000-0000-0000-000000000000/login/authorize?
client_id=a39796ab-75tg-po9f-3aa5-7yh22kj03a3
&redirect_uri=https://documentation.akamai.com
&scope=openid profile email
&response_type=code
&state=3bd5262737237ef4a

As noted, for a Hosted Login end user, the preceding activities are carried out by clicking a Login button that takes them to the login page. Once there, the user is asked to log on to their existing account, either by logging on to a social login identity provider (social login) or by supplying a username and password (traditional login).

After supplying their email address and password (in the case of a traditional login) the user clicks Sign In and authentication takes place. To the end user, nothing has changed: they still log on to your website the way they log on to most websites.

Meanwhile, the authorization server uses the supplied credentials (or the social login token received from the social identity provider) and attempts to log the user on.

The Redirect URI and Authorization Code

If the user is successfully authenticated, the authorization endpoint returns a redirect URI that looks similar to this:

https://v1.api.us.janrain.com/00000000-0000-0000-0000-000000000000/login/code?
state=security_token%3bd5262737237ef4a&
%url%https://documentation.akamai.com/callback&
code=4/JR27W91a-ofgCe9ur2m6bTghy77

There are two important things to note about this URL. First, the user’s authorization code is embedded within the URI itself:

https://v1.api.us.janrain.com/00000000-0000-0000-0000-000000000000/login/code?
state=security_token%3bd5262737237ef4a&
%url%https://documentation.akamai.com/callback&
code=4/JR27W91a-ofgCe9ur2m6bTghy77

This is the that code must be presented to the token exchange endpoint in order to retrieve the access, refresh, and identity tokens. For user to truly be logged on, the authorization code must be extracted from the response value and then presented to the token exchange endpoint.

Note: And that code must be presented soon: authorization codes are only valid for a few minutes. If your code expires before you request an access token you will need to restart the entire authentication process.

In addition to the authorization code, the URL also includes the anti-forgery state token:

https://v1.api.us.janrain.com/00000000-0000-0000-0000-000000000000/login/code?
state=security_token%3bd5262737237ef4a&
%url%https://documentation.akamai.com/callback&
code=4/JR27W91a-ofgCe9ur2m6bTghy77

This enables the OIDC client to verify that the response is authentic: if the anti-forgery state token included in the response matches the anti-forgery state token used in your original authentication request, then you can be reasonably sure that the redirect URL and the authorization code are valid. If the two do not match, that could indicate that a malicious actor is attempting to hijack the logon session.

Keep in mind that authorization codes can only be used once; if your access token has expired and you need to log on again, you’ll have to retrieve a new authorization code. If you try to reuse a code you’ll simply get back an error message similar to this one:
{
  "error": "invalid_grant",
  "error_description": "Invalid authorization code"
}

Exchanging the Authorization Code for an Access Token

After you get an authorization code, that code must be exchanged for an access token (along with the access token, you’ll get a refresh token and an identity token). To do that, the client contacts the token endpoint and makes an API request similar to this:
https://api.multi.dev.or.janrain.com/00000000-0000-0000-0000-000000000000/login/token
grant_type=authorization_code
&client_id=a39796ab-75tg-po9f-3aa5-7yh22kj03a3
&client_secret=6urBgvfgwb3kh67hdajk0sGGaaq1
&redirect_url=https://documentation.akamai.com
&code=tpKqJ7c_g2bOKBpl

In this request:

  • The grant_type parameter is set to authorization_code. This tells the token endpoint that the client would like to exchange an authorization code for a set of tokens.

  • The client_id parameter specifies the identity of the OIDC client. This must be the same OIDC client that made the original request.

  • The client_secret parameter specifies the secret (password) of the OIDC client.

  • The redirect_url parameter indicates where the user will be redirected after a successful logon. As noted elsewhere, the redirect URL must be the same URI used throughout the authentication process.

  • The code parameters transmits the authorization code.
If the authorization code is accepted, the token exchange endpoint returns an API response similar to this:
{
   "access_token": "03v-eeodppPrrHXXIx56pRLyDBaOldDxqEwI59MFCFGVuSkLRapzgmfwmEHyKWle",
   "refresh_token": "uHs1rLqRSpSyBpRpfplTI44Oh3gdkjJAa8Gzs3C5uDulN2yOnxU9mg1L6CaUAqz5",
   "expires_in": 3600,
   "token_type": "Bearer",
   "scope": "address email openid phone profile",
   "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImE5NjRhNjE3YTc0YjZjZWNlMDM4NTdkYWExZThlMTQ0ZDEx
MTMyYTkiLCJ0eXAiOiJKV1QifQ.eyJhdF9oYXNoIjoibklpWVRQaG9TaWs4Rmt0ZFl5cktZZyIsImF1ZCI6WyJhMjJjO
TYwNC03YjI3LTQ2NGYtYmZmNS04M2JhMjI5MzIzYWYiLCJodHRwczovL29wZW5pZGNvbm5lY3QubmV0L2NhbGxiYWNrI
l0sImF1dGhfdGltZSI6MTU1MjU5OTgyOCwiYXpwIjoiYTIyYzk2MDQtN2IyNy00NjRmLWJmZjUtODNiYTIyOTMyM2FmI
iwiZXhwIjoxNTUyNjAzNDQyLCJnbG9iYWxfc3ViIjoiY2FwdHVyZS12MTovL2NhcHR1cmUtYWxiLWJvcmRlci5tdWx0a
S5kZXYub3IuamFucmFpbi5jb20veDNnbW5uamV5enlycnQybm01ZHJmNW5rbjgvdXNlci8yZWRkMmYzMi0xZTQ5LTRiZ
jItYjE2NC03NjM3ODE3NjFiNTIiLCJpYXQiOjE1NTI1OTk4NDIsImlzcyI6Imh0dHBzOi8vYXBpLm11bHRpLmRldi5vc
i5qYW5yYWluLmNvbS8wMDAwMDAwMC0wMDAwLTMwMDAtODAwMC0wMDAwMDAwMDAwMDAvbG9naW4iLCJzdWIiOiIyZWRkM
mYzMi0xZTQ5LTRiZjItYjE2NC03NjM3ODE3NjFiNTIifQ.kKPbex5j3ADyxZ_t8B8wiWUoDB7o8tamMjswCxMQKaTEJB
pJBiYVATMdLvnd5HpZ5Hj_I0omt7Zq3svPFLvdy1xHC95KWyJu3HK65ZP8Hc0tM3oLFjWhLYcRoJZVi5ButzP4RZr6QJ
gfUyKF3QT-GECFLXgOyRy1DP4j4Xev7F_MJ_nX4xdAutNsDvu6PGyI752nS-4cJ13kAbyD0puaoLwg1aAoMSa4wm1lim
Pvv5HcnRAAZ-cyMQhaC13vHMnvCCRWzuHl94oNl2_ZblEtDQv_q_GfCvhXLrd1VH7azarkeOtCNrD1aTyQ9owXJDxYJr
cs2UTaop9tyA7_HgctWQ"
}

Here's what the different name-value pairs in that response represent:

Property Description
access_token The newly-issued access token.
refresh_token The refresh token that accompanies the access token.
expires_in

Amount of time (in seconds) before the access token expires. In this case, that's 1 hour (60 seconds x 60 minutes = 3,600 seconds).

Incidentally, identity tokens also expire after 1 hour (although that doesn’t matter too much because identity tokens are rarely used after they have been issued). Refresh tokens have a default lifespan of 90 days.

token_type Access token type. The token type will always be set to bearer, meaning that whoever has possession of the token is considered the rightful owner of that token. To gain access to resources, you only have to present the access token: you do not have to do anything to “prove” that the token belongs to you.
scope The OIDC scopes that the token has permission to retrieve. Scopes represent different sets of user profile attributes; for example, the profile scope enables you to return such things as the user’s name, his or her gender, his or her birthdate, etc.
Id_token The user’s identity token.

If you’re curious about the actual contents of a token, see the article Hosted Login Token Reference. In addition to that, you can decode an access token or a refresh token by using the introspection endpoint, and you can use any of a number of different JSON Web Token(JWT) decoders in order to view the contents of an identity token.