OpenID Connect Scopes and Claims


Scopes and claims represent the user information that a Hosted Login client requests from a server. For our purposes, each claim is equivalent to one piece of user information: the user’s first name is a claim, the user’s middle name is a claim, and the user’s last name is a claim. (And, of course, the user’s full name – Karim J. Nafir – is yet another claim.) As we’ll see in the section Mapping Hosted Login Claims to User Profile Attributes, each claim typically maps to a single user profile attribute.

Note. We say “typically” because, well, that’s typically what we mean when we use the word “claim.” Technically, however, pretty much any name/value pair in OpenID Connect (OIDC) is referred to as a claim. Because of that, there will be times when you’ll see the word claim even though we’re obviously not talking about user information. For example, the iss claim in an access token is the URL of your authorization endpoint, something not directly associated with user information.

Scopes, in turn, are nothing more than collections of claims: scopes a provide a shortcut method for returning multiple pieces of user information. For example, instead of returning three separate claims – first name, middle name, and last name – you might have a single scope (e.g., userNames) that returns all three names.

OIDC defines several standard scopes, all of which are supported by Hosted Login (albeit with some slight variations here and there):

  • openid. Identifies your request as an OIDC request. This scope must be included in all your authentication requests.
     
  • profile. Requests access to the following attributes (note that these are OIDC attribute names and not Akamai Identity Cloud attribute names): 
    • name
    • family_name
    • given_name
    • middle_name
    • nickname
    • preferred_username
    • gender
    • birthdate
    • updated_at
       
  • email. Requests access to the following attributes:
    • email
      email_verified 

       
  • address. Requests access to the user's mailing address.
     
  • phone. Requests access to the following attributes:
    • phone_number
    • phone_number_verified

Note that you can include multiple scopes in your API calls simply by separating the scopes with a blank space. For example, this call requests three scopes:

scope=openid email phone

Supported scopes and supported claims are both included in your discovery document.




Using Scopes                    

Back to Top

To return a scope (or two or three or ...), you need to include those scopes in your authorization request. For example, this request includes the openid scope (which must be included in all authorization requests) as well as the email scope:


https://v1.api.us.janrain.com/e0a70b4f-1eef-4856-bcdb-f050fee66aae/login/authorize
    ?client_id=a123ef65-83dc-4094-a09a-76e1bec424e7
    &redirect_uri=https://oidc-playground.akamai.com/redirect_uri
    &scope=openid email
    &code_challenge=1xu_tzZRnOCFJvHheszOExwuCEe1rDr38py39TkZwzk
    &code_challenge_method=S256
    &response_type=code
    &state=skxAi1rcAxQXbn5pgHdGuyd-PoNrusDY-0-e_KFEclg

After the user has logged in, you can make a call to the userinfo endpoint and return, among other things, the two claims (email and email_verified) that comprise the email scope:

{
    "email": "karim.nafir@mail.com",
    "email_verified": true,
    "global_sub": "capture-v1://se-demos-gstemp.us-dev.janraincapture.com/79y4mqf2rt3bxs378kw5479xdu/GREG_DEMO/3c388dd9-5bcc-4883-9a91-d51129110a4a",
    "sub": "3c388dd9-5bcc-4883-9a91-d51129110a4a"
}
Note. We should mention that scope information is only accessible from the userinfo endpoint; returned scopes aren’t added to the identity token. If you want to make user information available from the identity token, you’ll need to use claims instead of scopes.

That looks pretty straightforward: you ask for a scope, and, after the user logs on, that scope is accessible from the userinfo endpoint. Case closed!

Except for one thing: the case is not closed. Consider this authorization request, which asks for both the email scope and the address scope:


https://v1.api.us.janrain.com/e0a70b4f-1eef-4856-bcdb-f050fee66aae/login/authorize
    ?client_id=a123ef65-83dc-4094-a09a-76e1bec424e7
    &redirect_uri=https://oidc-playground.akamai.com/redirect_uri
    &scope=openid email address
    &code_challenge=1xu_tzZRnOCFJvHheszOExwuCEe1rDr38py39TkZwzk
    &code_challenge_method=S256
    &response_type=code
    &state=skxAi1rcAxQXbn5pgHdGuyd-PoNrusDY-0-e_KFEclg

So what do you see if you check the userinfo endpoint after the user logs on? You see this:

{
    "email": "karim.nafir@mail.com",
    "email_verified": true,
    "global_sub": "capture-v1://se-demos-gstemp.us-dev.janraincapture.com/79y4mqf2rt3bxs378kw5479xdu/GREG_DEMO/3c388dd9-5bcc-4883-9a91-d51129110a4a",
    "sub": "3c388dd9-5bcc-4883-9a91-d51129110a4a"
}

The address scope has not been returned. Why not?

The reason for that is simple. To begin with, you can only get back (or potentially get back) scopes that you ask for (i.e., scopes that were included in your authorization request). For example, we didn’t ask for the profile scope, so it shouldn’t come as a surprise that we didn’t get back the profile scope.

But we did ask for the address scope, and we didn’t get that back, either. Yes, admittedly, you can ask for any scope you want; heck, you can even ask for scopes that don’t exist:

&scope=openid email bob

However, you can only get back scopes that you ask for and are included in the allowedScopes property in your token policy. As it turns out, the token policy we’re been using only includes openid and email in its collection of allowed scopes:

"allowedScopes": [
    "openid",
    "email"
    ],

That’s why the address scope wasn’t returned: the token policy doesn’t allow it to be returned. If you want to be able to use the OIDC login client a123ef65-83dc-4094-a09a-76e1bec424e to return the address scope (that, by the way, is the login client we’ve been using) then you’ll need to make sure that the token policy associated with that login client includes address in the set of allowed scopes:

 "allowedScopes": [
    "openid",
    "email",
    "address"
    ],
Note. You can use the /{customerId}/config/tokenPolicies/{tokenPolicyId} endpoint to modify the allowedScopes property.

If a scope isn’t in the token policy then that scope won’t be accessible from the userinfo endpoint (period). However, if we do add address to the token policy we can then get back address information from the userinfo endpoint:

{
    "address": {
        "country": "US",
        "formatted": "1233 NW 12th Ave #150 \nPortland, OR 97209\nUS",
        "locality": "Portland",
        "postal_code": "97209",
        "region": "OR",
        "street_address": "1233 NW 12th Ave #150"
        },
    "email": "karim.nafir@mail.com",
    "email_verified": true,
    "global_sub": "capture-v1://se-demos-gstemp.us-dev.janraincapture.com/79y4mqf2rt3bxs378kw5479xdu/GREG_DEMO/3c388dd9-5bcc-4883-9a91-d51129110a4a",
    "sub": "3c388dd9-5bcc-4883-9a91-d51129110a4a"
}

Think of it like this. There’s a clothing store down the street that sells red shirts, blue shirts, and green shirts. You walk into that store, and ask to try on a red shirt and blue shirt. The salesperson will hand you a red shirt and blue shirt, but won’t hand you a green shirt, Why not? Because you didn’t ask for a green shirt. The clothing store might also sell pants and coats and hats, but you won’t get any of those, either, because you didn’t ask for them. That’s how an authorization request works: you can only get what you ask for. (And you don’t have to ask for everything, either. Just ask for the scopes you need.)

Now, suppose you ask to try on a red shirt, a blue shirt, and a purple shirt. The salesperson will hand you a red shirt and a blue shirt, but won’t hand you a purple shirt. Why not? Because the store doesn’t sell purple shirts. And that’s kind of how the token policy works: if the token policy doesn’t allow a particular scope then you can’t get that scope back. In this case, the scope is like the purple shirt: to the token policy, at least, that scope is not for sale. A clothing store can only sell you the shirts they stock. An authorization request can only bring back the scopes specified in the token policy.

Here’s a table that walks through some common scope-requesting scenarios:

You ask for these scopes in your authorization request

The token policy specifically allows these scopes

After login, these scopes will be accessible from the userinfo endpoint


  • email
  • address



  • email
  • address
  • profile
  • phone



  • email
  • address

You get back email and address because they’re in both the authorization request and the token policy.


  • email
  • address



  • email
  • phone



  • email

You get back email because it’s in both the authorization request and the token policy. You don’t get back address because it’s not in the token policy. You can’t get it back if it’s not in the token policy.


  • email
  • address



  • profile
  • phone




You don’t back anything because neither email nor address is in the token policy.



Using Standard Claims

Back to Top


By default, Hosted Login supports 16 claims, most of which map directly to an Identity Cloud user profile attribute. These claims are summarized in the following table:

Claim

Scope

Datatype

User Profile Attribute

sub

Subject. Unique identifier for the end user. This will always be the user’s Identity Cloud UUID.

openid

string

uuid

iss

Issuer Identifier. URL of your login server. For example: 

https://v1.api.us.janrain.com/00000000-0000-3000-8000-000000000000/login

openid

string

--

auth_time

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

openid

number

lastLogon

name

User’s full name, including titles and suffixes, ordered according to the user’s locale and preferences. For example: Dr. Toni Ng.

profile

string

--

given_name

Given name (first name) of the user. 

profile

string

givenName

family_name

Surname (last name) of the user. 

profile

string

familyName

middle_name

Middle name of the user. 

profile

string

middleName

preferred_username

Name by which the user wishes to be referred, such as Karim Nafir, K. Nafir or karim_n

profile

string

displayName

gender

User’s gender. 

profile

string

gender

birthdate

The user’s birthday, represented in ISO8601‑2004 format (e.g., 1967-07-12, or YYYY-MM-DD). If the year is set to 0000 (0000-07-12) that indicates that the user chose to enter the month and day, but not the year, of their birth.

profile

string

birthday

updated_at

Indicates the last time that the user profile was updated. The updated_at 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.

profile

number

lastUpdated

address

User’s preferred mailing address. The JSON format for the address claim looks like this:

{
  "address": {
    "country": "country",
    "formatted": "address1 address2\ncity, zip\ncountry",
    "locality": "city",
    "postal_code": "zip",
    "region": "",
    "street_address": "address1 address2"
    }
}

In the preceding JSON value, the \n indicates a line break.

address

string

Derived from primaryAddress

phone_number

User’s preferred cell phone (mobile) number. I

phone

string

mobileNumber

phone_number_verified

Set to true if the user’s phone number has been verified. When this value is true, it means that steps were taken steps to ensure that the phone number was controlled by the user at the time the verification was performed.

phone

boolean

Derived from mobileNumberVerified

email

User’s preferred email address. 

email

string

email

email_verified

Set to true if the user’s email address has been verified. When this value is true, it means that steps were taken steps to ensure that the email address was controlled by the user at the time the verification was performed. This is typically done by having the user respond to a “Verify Email Address” email.

email

boolean

Derived from emailVerified

See the section Adding Claims to an Authorization Request for information on actually using these standard claims.



Creating Custom Claims

Back to Top


In addition to the standard claims, you can create as many custom claims as you might need; this allows you to return information for just about any attribute stored in your user profiles.

For example, suppose you have created three custom attributes in your user profile schema:

  • email_marketing_optIn
  • ui_preferences_optIn
  • personalized_ads_optIn

Let’s further suppose that you’d like to make these attributes available any time a user logs on. To do that, all you have to do is add a customClaims section to your login policy. For example:

{
  "customClaims":
    {"id_token": 
      {
        "consent_email_marketing": "email_marketing_optIn",
        "consent_ui_preferences": "ui_preferences_optIn",
        "consent_personalized_ads": "personalized_ads_optIn"
      }
    }
}

When creating custom claims, these claims must be added either to the identity token (id_token) or to the user profile information that can be retrieved from the userinfo endpoint. As the name implies, id_token claims are attached to the identity token and can be retrieved directly from that token. By comparison, to return userinfo claims you must contact and retrieve the information from the userinfo endpoint.

Each custom claim must also include a unique name mapped to a single user profile attribute. For example, this syntax maps the claim consent_email_marketing to the user attribute email_marketing_optIn:

"consent_email_marketing": "email_marketing_optIn"

Note that the custom claim names simply have to be unique; otherwise there are no special restrictions. For example, we could just as easily map consent_email_marketing to a claim named marketing:

"marketing": "email_marketing_optIn"

Or even to a claim named x:

"x": "email_marketing_optIn"

However, your attribute names (as opposed to your claim names) can’t be arbitrary: you must use the exact name and the exact letter casing employed in your user profile schema. For example, this syntax, which uses all uppercase letters for the attribute name, will fail:

 "consent_email_marketing": "EMAIL_MARKETING_OPTIN"

You can create as many custom claims as you need, but you cannot collect those claims into a custom scope. To return the consent attributes used in this example, you must request each claim separately; you cannot create a custom scope (e.g., consents) and request all those claims in a single operation. Contact your Akamai representative for more information.


Adding Claims to an Authorization Request

Back to Top


Claims can be added to an authorization request by using the aptly-named claims parameter. For example, this authorization request uses the claims parameter to make the gender claim accessible from the userinfo endpoint and, at the same time, copy  the gender claim to the identity token:

https://v1.api.us.janrain.com/e0a70b4f-1eef-4856-bcdb-f050fee66aae/login/authorize
   ?client_id=a123ef65-83dc-4094-a09a-76e1bec424e7
   &redirect_uri= https://oidc-playground.akamai.com/redirect_uri
   &scope=openid
   &code_challenge=ZJvyt3-dkp_mmf6VWUiRiG_8O3QxQswrNs99Zlk7khU
   &code_challenge_method=S256
   &response_type=code
   &claims={"userinfo":{"gender":null},"id_token":{"gender":null}}
   &state=mclPck7S-uMvEi8EVZyPIyYHKABav8SScGMEyI3jc3o

   
In other words, we really have the following two claims, separated by a comma, and enclosed in curly braces:]

  • "userinfo":{"gender":null}
  • "id_token":{"gender":null}

If you’re wondering how to interpret this syntax:

  • userinfo indicates that the data should be made accessible from the userinfo endpoint. Likewise, id_token indicates that the claim should be added to the identity token.
     
  • gender is the name of the claim. Keep in mind that claim names are case-sensitive. If we ask for the Gender claim our authorization request won’t result in an error, but the authorization server will also ignore the non-existent Gender claim.

    We should also add that you request custom claims using the exact same syntax as you do with standard claims. Do you want to add a custom claim named organization to the identity token? Then use this syntax:
"id_token":{"organization":null}
  • null indicates that we want the claim returned in the default manner. There are other ways to return custom claims, but none of those are of interest to us at the moment.

If you want to return multiple claims, just specify those additional claims as part of a comma-separated list. For example, here we’ve asked for both gender (a standard claim) and organization (a custom claims):

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

And there’s no reason why claims have to be accessible from the userinfo endpoint and added to the identity token. Prefer to just make your claims accessible from the userinfo endpoint? Then leave out id_token:

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

On a similar note, you can make one claim (gender) accessible from the userinfo endpoint and a totally different claim (organization) can be added to the identity token:

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

The takeaway? You have considerable flexibility when it comes to requesting claims.