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, none 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) Works

Back to top


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 Authorization Request

Back to top


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

Back to top

Unique identifier of the OIDC login client used to make the authorization request. For example:

cliient_id=22c9604-7b27-464f-bff5-83ba229323af

response_type 

Yes

Back to top

Specifies the type of response expected from the authorization server; at this point in time the Identity Cloud only supports the code response type. Note that this parameter is required even though there’s only one supported response type:

response_type=code

The code response indicates that the client expects to get an authorization code back following a successful authentication. In turn, the client exchanges that code for a set of tokens.

scope

Yes

Back to top

Specifies the OpenID Connect scopes to be accessible from the userinfo endpoint following a successful authentication and login. Note that you must include the scope parameter and, at a minimum, request the openid scope; this tells the authorization server that you want to authenticate by using OpenID Connect.

Other scopes supported by the Identity Cloud are detailed in the article OpenID Connect Scopes and Claims. You can request multiple scopes by separating each scope using a blank space:

scope=openid email profile

You can include any (or all) the supported scopes in your authentication request. However, that doesn’t mean that you’ll get back all of those scopes. Instead, the scopes made accessible from the userinfo endpoint depend on the value of the allowedScopes property found in the token policy applied during a user login. 

For example, suppose the allowedScopes property only specifies the openid and email scopes. In that case you can only get back those two scopes; any other scopes mentioned in your authorization request (such as profile or address) are ignored and are not returned.

redirect_uri

Yes

Back to top

Specifies the URL of the page the user is redirected to following a successful authentication and login. For example:

redirect_uri=https://identitydocs.akamai.com/redirect

Note that the specified URL must be exactly match one of the URLs listed in the OIDC login client’s redirectURIs property. If the URL isn’t included in the redirectURIs property then the authorization request fails with an Invalid client error and the user will not be authenticated.

state

No (but recommended) 

Back to top

A random string that helps guard against cross-site request forgery (CSRF). For example, suppose your authentication includes the following state parameter and parameter value: 

state=GA-ISU_6CwFn0tQTFiYD_-Gvy39Nb6iTdugdGIzTUng

After a successful authentication, you’ll be redirected to the URL specified by the redirect_uri parameter. If you were redirected by the authorization server then the state parameter and value will be included in the URI:

https://identitydocs.akamai.com/redirect_uri?code=AH6S2WG_XchALC-p&state=GA-ISU_6CwFn0tQTFiYD_-Gvy39Nb6iTdugdGIzTUng

If the state parameter in the redirect URI doesn’t match your original parameter value then you might be the victim of CSFR attack (defined as an attack in which malware tries to trick you into carrying out some sort of action you never intended to carry out). In that case, you should restart the authentication process.

prompt

No

Back to top

Specifies which screen (if any) is displayed when a user makes an authorization request. Allowed values are:

  • none. When prompt is set to none, Hosted Login first checks to see if the client has a valid session. If a valid session is found the user doesn't need to authenticate; instead, he or she is automatically logged in using the existing session. If a valid session can't be found a "No authenticated session found" error is generated and the user is not given the option of logging in.

    If you set the prompt parameter to none, it's recommended that you write code that: 1) looks for the "No authenticated session found" error ; and, 2) displays the sign-in screen. (If you don't, a user without an existing session would never be able to log in.) You might consider creating a cookie indicating that the user has been denied access because they didin't have a valid session. If you do that then, the next time user accesses your site , you'll know to employ an authorization request where the prompt is set to login (i.e., a request where the sign-in screen is always displayed).
     
  • login. The sign-in screen is always displayed first, even if a valid session is found. This ensures that users log in each time they access the site.
     
  • create. The traditional registration screen (used for creating new account) is always displayed first. Note, however, that the Sign In link isn’t found on the traditional registration screen. That means that setting the prompt to create represents a dead-end for existing users: they don’t need to create account, but they can’t log on using their existing account.

If this parameter isn't included then Hosted Login first checks to see if the client has a valid session. If a valid session exists the user doesn't need to authenticate; instead, he or she is automatically logged in using the existing session. If a valid session can't be found then the sign-in screen is displayed and the user can log in.

For example:

prompt=login

max_age

No

Back to top

Specifies the amount of time, in seconds, that can elapse before a user is required to reauthenticate. For example, suppose the max_age parameter is set to 3600 seconds (one hour). A user logs on, leaves the website, then returns 30 minutes later. Because the max_age limit of 1 hour has not been reached, the user will automatically be authenticated and resume their previous session.

Now, suppose a second user logs on, leaves the website, then comes back 2 hours later. because the max_age value has been exceeded, this user will be forced to reauthenticate.

Note that the max_age parameter applies only to logins. Suppose a third user logs on and stays on the site for 2 hours. That user will not be forced to reauthenticate halfway through their session. As noted, max_age only applies to logins.

ui_locales

No

Back to top

Specifies the language/locale used when displaying Hosted Login login, registration, and user profile screens. Language preferences are passed as a space-delimited set of RFC 5646 language codes. For example:

ui_locales= fr-FR es-ES

In the preceding example, Hosted Login first tries to render screens by using French (fr-FR); if that fails, Hosted Login tries to render the screens by using Spanish (es-ES). If that fails, then Hosted Login defaults to displaying all screens in English.

Why would an attempt to render screens fail? This is almost always because you specified a language/locale that can’t be found in your flow: you can specify any language or locale that you want, but to actually display screens using that language/locale requires you to have the locale (and the accompanying translations) in your flow. See this article for more information.

nonce

No (but recommended)

Back to top

Helps ensure that the identity token you receive is the same identity token that you requested (in other words, you got back a token sent in direct response to your authentication request). 

To use the nonce parameter, simply enter a random string in the Nonce field and then make your authentication request, When you decode the returned identity token, you should see a nonce property. The value in the identity token should be the same as the value included in your authentication request.

login_hint

No

Back to top

Provides a way to prepopulate the email address field on the Hosted Login sign-in screen. In your authorization request, include the login_hint parameter followed by the email address of the user who needs to be authenticated. For example:

https://v1.api.us.janrain.com/ e0a70b4f-1eef-4856-bcdb-f050fee66aae/login/authorize?
&client_id=a123ef65-83dc-4094-a09a-76e1bec424e7
&redirect_uri=https%3A%2F%2Fwacky-harmonious-bike.dev.or.janrain.com%2Fredirect_uri
&code_challenge=MJm7VEGLvMtD4Mi1SGUc2QPRPVKqyaoEbBTxYKC4UJk
&code_challenge_method=S256
&response_type=code
&scope=openid
&state=5TK5-3LXryr8EIxn6kV4mgqEa3KqRA4-HwHJbyzlgU0
&login_hint=gmstemp@hotmail.com

When you submit your authorization request, the email address will be included on the sign-in screen:

Note that Hosted Login cannot determine the email address to be included in the authorization request. Instead, you will need to use an alternate approach to determine the email address (for example, getting the email address when the user logs on to the computer) and then take the steps needed to add that address to the authorization request.

display


No

Back to top

Specifies where (and how) the sign-in screen is displayed. Allowed values are:

  • page (the default value)., When you submit your authorization request, you’ll be redirected to a separate page that contains the sign-in screen (and nothing but the sign-in screen). After you’ve successfully logged on you'll be redirected to the page specified in the redirectURIs property of your OIDC client.
     
  • popup. When you submit your authorization request you are not redirected to a standalone login page. Instead, the login page appears in a pop-up window, no redirection required. After you’ve successfully logged on then you’ll be redirected to the page specified in the redirectURIs property of your OIDC client.

For example:

display=page

claims

No

Back to top

Specifies the claims (i.e., user profile attributes) to be included in the identity token or to be made accessible from the userinfo endpoint (or both). These claims can either be standard OpenID Connect claims (see OpenID Connect Scopes and Claims for more information) or custom claims created by your organization and defined in your login policies.

For example, this syntax makes the birthdate claim accessible from the userinfo endpoint:

&claims={"userinfo":{"birthdate":null}}

Meanwhile, this syntax adds a custom claim named organization to the identity token:

&claims={"id_token":{"organization":null}}

And this syntax makes the organization claim accessible from the userinfo endpoint and adds that same claim to the identity token:

&claims={"userinfo":{"organization":null}, "id_token":{"organization":null}} 

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

Back to top


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=4JR27W91a-ofgCe9ur2m6bTghy77

There are three 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://hosted-login.akamai.com/callback&code=4JR27W91a-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&code=4JR27W91a-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.

Last, but far from least, the response also includes the redirect URL, which indicates where the user will be redirected after a successful logon:

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

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

Back to top


Note. The following discussion applies only to confidential clients. For public clients, see the article Authorization Code + PKCE for Mobile Apps.

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 the following Curl command:


curl -X POST \
https://api.multi.dev.or.janrain.com/00000000-0000-0000-0000-000000000000/login/token \
  -H 'Authorization: Basic RcaWTi0woO52rqZjlbApm2lL3Aokzd1bhCZZajX51MbR26eZHwtEqaw9RLMBeIJDvqvqyD4l' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=authorization_code' \
  -d 'client_id=a39796ab-75tg-po9f-3aa5-7yh22kj03a3' \
  -d 'redirect_uri=https://documentation.akamai.com' \
  -d 'code=4JR27W91a-ofgCe9ur2m6bTghy77'
 

In this request:

  • Basic authentication is required. Use the client ID of the OIDC client as your username and the client secret of that same client as your password.
  • All parameters must be passed as xxx-www-urlencoded body parameters.
  • 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 redirect_uri 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": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImE5NjRhNjE3YTc0YjZjZWNlMDM4NTdkYWExZThlMTQ0ZDExMTMyYTkiLCJ0eXAiOiJKV1QifQ.eyJhdF9oYXNoIjoibklpWVRQaG9TaWs4Rmt0ZFl5cktZZyIsImF1ZCI6WyJhMjJjOTYwNC03YjI3LTQ2NGYtYmZmNS04M2JhMjI5MzIzYWYiLCJodHRwczovL29wZW5pZGNvbm5lY3QubmV0L2NhbGxiYWNrIl0sImF1dGhfdGltZSI6MTU1MjU5OTgyOCwiYXpwIjoiYTIyYzk2MDQtN2IyNy00NjRmLWJmZjUtODNiYTIyOTMyM2FmIiwiZXhwIjoxNTUyNjAzNDQyLCJnbG9iYWxfc3ViIjoiY2FwdHVyZS12MTovL2NhcHR1cmUtYWxiLWJvcmRlci5tdWx0aS5kZXYub3IuamFucmFpbi5jb20veDNnbW5uamV5enlycnQybm01ZHJmNW5rbjgvdXNlci8yZWRkMmYzMi0xZTQ5LTRiZjItYjE2NC03NjM3ODE3NjFiNTIiLCJpYXQiOjE1NTI1OTk4NDIsImlzcyI6Imh0dHBzOi8vYXBpLm11bHRpLmRldi5vci5qYW5yYWluLmNvbS8wMDAwMDAwMC0wMDAwLTMwMDAtODAwMC0wMDAwMDAwMDAwMDAvbG9naW4iLCJzdWIiOiIyZWRkMmYzMi0xZTQ5LTRiZjItYjE2NC03NjM3ODE3NjFiNTIifQ.kKPbex5j3ADyxZ_t8B8wiWUoDB7o8tamMjswCxMQKaTEJBpJBiYVATMdLvnd5HpZ5Hj_I0omt7Zq3svPFLvdy1xHC95KWyJu3HK65ZP8Hc0tM3oLFjWhLYcRoJZVi5ButzP4RZr6QJgfUyKF3QTGECFLXgOyRy1DP4j4Xev7F_MJ_nX4xdAutNsDvu6PGyI752nS4cJ13kAbyD0puaoLwg1aAoMSa4wm1limPvv5HcnRAAZcyMQhaC13vHMnvCCRWzuHl94oNl2_ZblEtDQv _q_GfCvhXLrd1VH7azarkeOtCNrD1aTyQ9owXJDxYJrcs2UTaop9tyA7_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.