Mapping Attributes

One of the advantages to social login is the fact that users can log on to your website without having to create a new username and a new password tied specifically to that site. Instead, they can use an existing account previously created on a different website. For example, if users are already accustomed to logging on to Facebook or Twitter, they can use their Facebook or Twitter account to log on to your website as well. (Assuming, of course, that you allow them to do this.)

There’s also another advantage to social login: not only can a user employ a previously-created user account, but they can also leverage the user profile associated with that existing account. (Depending on the information the identity provider allows you to return.) When creating a custom provider you have the option of adding an “attribute map” to the new provider. This map takes user profile information returned in a SAML assertion or a JSON API response and copies that information to the user’s Identity Cloud user profile.

Note. What if you don’t add an attribute map to your custom provider? That’s fine: the user can still use the social login provider to log on to your site. However, nothing will be copied from the SAML assertion or the JSON response to the user’s Identity Cloud profile.

For example, suppose we have a SAML assertion that includes several attributes (Firstname, Lastname, and Email) correlating to standard Identity Cloud user profile attributes (givenName,familyName, and email). In an ideal world, we’d extract the user information from the SAML assertion, then copy that information to the user’s Identity Cloud user profile. In that case: 1) the user doesn’t have to create a new username and password for your site (they log on with their SAML account instead); and, 2) the user doesn’t have to add their first and last names and their email address to their Identity Cloud profile. Instead, that information is copied over for them from their SAML account

As it turns out, we can copy user information from the SAML assertion to the user’s Identity Cloud profile: that’s what attribute mapping is all about. All we have to do is specify which SAML assertion attributes correspond to which Identity Cloud attributes. In more graphical terms, we need to connect the SAML assertion attributes (shown on the left) with their Identity Cloud equivalents (shown on the right):

Correlating the two sets of attributes is the job of the attribute_map parameter. The attribute_map parameter is a collection of JSON key-value pairs that map an Identity Cloud attribute (on the left) to a SAML assertion attribute (on the right). For example, with our sample assertion the attribute_map looks like this, with the Identity Cloud attributes shown in red, and the SAML assertion attributes shown in green:

{
  "attribute_map": {
    "/email": "/email",
    "/givenName": "/Firstname",
    "/familyName": "/Lastname"
  }
}


Note. Yes, the forward slashes that precede each attribute name (e.g., /Firstname) are required. We’ll talk more about those slashes in a few minutes.

What if our SAML assertion included more than just those three attributes? That’s fine; we can just add those “extra” attributes to the list:

{
  "attribute_map": {
    "/email": "/email",
    "/givenName": "/Firstname",
    "/familyName": "/Lastname",
    "/primaryAddress.organization": "/Companyname",
    "/primaryAddress.country": "/Countryofresidence",
  }
}

Etc., etc.

When you create an attribute map, keep in mind that attribute mapping is a largely-forgiving process; that’s something that has both its advantages and its disadvantages. For example, suppose you mistype an attribute name: maybe you list the SAML assertion attribute as /Fristname. Obviously that’s not going to return any data: the SAML assertion doesn’t have an attribute by that name. However, a mistake like that won’t return an error and won’t interfere with the authentication process: if a given attribute can’t be found then a null value is returned. In this example, that means that the user’s givenName attribute in their Identity Cloud user profile is set to null. The user still gets logged on: they just won’t have a first name listed in their user profile.

Of course, that also means that you should always check for misspellings if don’t get back the data (like a user’s first name) you expected to get back. You also have to watch for letter casing issues: the Identity Cloud attribute is givenName rather than givenname or GivenName. Again, errors like that won’t cause your API call to fail, but they will keep you from copying the expected data to your user profiles.

Incidentally, the following names are reserved and, because of that, can’t be used as Identity Cloud user profile attributes (at least not when creating an attribute map): identifier, providerName, and providerSpecifier.



Mapping OAuth and OpenID Connect Attributes


With both OAuth and OIDC, user profile data is returned in JSON (JavaScript Object Notation) format. For example, you might get user back profile information that looks like the following, with the attribute names used by the social login provider shown in green:

{
    "first_name": "Karim",
    "last_name": "Nafir",
    "birthdate": "10/18/1960",
    "email_address": "karim.nafir@mail.com",
    "email_verified": true
}

To map these elements to Akamai user profile attributes, the Identity Cloud uses JSON Pointer (JPointer), a methodology for locating specific elements in a JSON document. For example, the following example shows how the attributes found in the preceding JSON file (displayed in green) are mapped to their Identity Cloud user profile counterparts (shown in red):

{
  "attribute_map": {
    "/givenName": "/first_name",
    "/familyName": "/last_name",
    "/birthday": "/birthdate",
    "/email": "/email_address",
    "/emailVerified": "/email_verified"
  }
}
Note. How do you know the attribute names used by the social login IdP? Typically you’ll find these names (and the kind of data they return) documented on the IdP’s developer website. If you can’t find them, you can simply make an API call against the site and see what comes back.

As noted a moment ago, attributes are mapped using key-value pairs, with the Identity Cloud attribute (e.g., givenName) serving as the key and the JSON attribute name (first_name) functioning as the value. Note, too that each mapped value must be prefaced by using a forward slash (/); that’s why we see values like /givenName and /familyName

JPointer has very few restrictions when it comes to JSON attribute names; that means that most mappings should be pretty straightforward. For example, you probably won’t encounter many attribute names that include blank spaces (e.g., given name). If you do, however, JPointer  can map those names just as easily as it can map any other name:

"/givenName": "/given name"

In fact, about the only things you need to watch out for are the following:

  • The tilde (~) is a reserved character in JPointer, which means it must be “escaped” if it appears in an attribute name (given~name). That simply means that the tilde character must be replaced by ~0:
    "/givenName": "/given~0name"
    To be honest, the odds are pretty good that you won’t ever encounter an attribute name that includes a tilde. But, if you do, you’ll be able to map it.

  • The forward slash (/) is also a reserved character in JPointer; because of that, any forward slashes in an attribute name (given/name) must be escaped by using ~1:
    "/givenName": "/given~1name"
    This is another construction you’re unlikely to run into. But you never know.

However, you could encounter JSON arrays where you want to map only a specific item in that array as opposed to the entire array (for example, only the first item or only the last item). To do that, just remember that the first item in a JSON array is assigned the index number 0, the second item in the array is assigned the index number 1, and so on. For example, suppose your JSON array looks like this:

 "favorite_color": ["red", "yellow", "blue", "green"]

Index numbers for this sample array are:

Index Number

Value

0

red

1

yellow

2

blue

3

green


To map an attribute to the first value in the list (red) use this syntax:

"/favoriteColor": "/favorite_color/0"

To map an attribute to the third item in the list (blue, with the index number 2), use this syntax:

"/favoriteColor": "/favorite_color/2"

Another thing to watch out for when mapping JSON attributes are “nested” attributes: attributes contained as values within another attribute. For example, suppose the JSON response includes the following:

"user_address": {
    "city": "Portland",
    "state": "OR",
    "country": "US"
}

How do you map the country attribute? You might think this will do the trick:

"/primaryAddress.country": "/country"

As it turns out, however, that won’t do the trick: that’s because, technically speaking, the JSON response doesn’t have an attribute named country. Instead, you must reference the entire attribute path: /user_address/country. That's how map a nested attribute:

"/primaryAddress.country": "/user_address/country"

Note that, this case, you don’t escape the forward slash. That’s because the forward slash is part of the attribute path and not part of the attribute name.



Mapping SAML Attributes


When you authenticate by using a SAML 2.0 identity provider, the provider returns a “SAML assertion,” an XML document that includes information about the attempted authentication. For example, if authentication succeeded you’ll see a Status message similar to this:

<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</samlp:Status>

Assuming that authentication was successful, your SAML assertion will also include a <saml:AttributeStatement> section. This section contains the user profile attributes returned from the social login provider (i.e., the user profile information stored by the IdP). For the moment, we’re going to focus on two items within this section:

  • The Name property, which indicates the name of the attribute as used by the IdP. For example, the user’s first name might be stored in an attribute labeled Firstname.

  • The <samlAttributeValue> property, which specifies the value of the given attribute (e.g., Firstname) as stored in the user’s user profile. For example, the Firstname attribute might have the value Greg.

Here’s a sample (and color-coded) <saml:AttributeStatement> section. Attribute names are shown in red, and attribute values are shown in green:

<saml:AttributeStatement>
     <saml:AttributeName="Userid" NameFormat=" urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
          <saml:AttributeValue Type="xs:string">
0c02a89a-f296-4550-9fad-055cf87099f4
          </saml:AttributeValue>
     </saml:Attribute>
     <saml:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
          <saml:AttributeValue Type="xs:string">
gmstemp@hotmail.com
          </saml:AttributeValue>
     </saml:Attribute>
     <saml:Attribute Name="Firstname"NameFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
          <saml:AttributeValue Type="xs:string">
Greg
          </saml:AttributeValue>
     </saml:Attribute>
     <saml:Attribute Name="Lastname" NameFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
          <saml:AttributeValue Type="xs:string">
Stemp
          </saml:AttributeValue>
     </saml:Attribute>
     <saml:AttributeName="loginMethod"NameFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
          <saml:AttributeValue Type="xs:string">
traditionalSignin
          </saml:AttributeValue>
     </saml:Attribute>
</saml:AttributeStatement>

In general, SAML assertions are easy to decipher. For example, the following snippet simply tells us that the user profile includes an attribute that stores the user’s first name (Firstname):

<saml:Attribute Name="Firstname" NameFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">

Meanwhile, this snippet tells us that the user who just authenticated (i.e., the user who was issued this assertion) has the first name Greg):

<saml:AttributeValue Type="xs:string">
Greg
</saml:AttributeValue>

When you map an Identity Cloud attribute to a SAML assertion, you use the same format as you do when mapping an Identity Cloud attribute to a JSON document. For example:

{
  "attribute_map": {
    "/email": "/email",
    "/givenName": "/Firstname",
    "/familyName": "/Lastname"
  }
}

In the preceding attribute map, the Identity Cloud user attribute (e.g., givenName) is shown in red, and the SAML assertion attribute (for example, Firstname) is shown in green. Note that, in both cases, attributes names are prefaced by using a forward slash: you refer to the /givenName attribute instead of the givenName attribute.

When extracting data from a SAML assertion, the Identity Cloud uses a subset of XPointer, a technology that locates specific items in an XML document. By design, the custom provider’s pointer is “rooted” on the saml:AttributeStatement element. That has two important implications:

  1. Attributes can only be returned if those attributes are inside the saml:AttributeStatement node (which will almost-always be the case for user profile attributes). When mapping attributes, there is no way to reference any other element within the SAML assertion. (In other words, if there is an attribute located elsewhere in the document you won’t be able to map that attribute to an Identity Cloud user profile attribute.)

  2. Because the root element has been predefined, you can use the shortcut method /email to refer to individual attributes. That syntax simply says, “Find the email attribute inside the saml:AttributeStatement element.”

Although unlikely, it’s possible to have two attributes with the same name (e.g., email) located within the saml:AttributeStatement element. If that happens then, by default, your mapping automatically uses the first instance of the email attribute and ignores the second instance.

It’s also possible for a single attribute to contain multiple values; for example, a Favoritecolors attribute might include these values:

Color

purple

yellow

red

blue

When an attribute contains a collection of values, you can specify which value to map to by using indexing. When working with XPointer, the first item in a collection is assigned the index number 1, the second item in the collection is assigned the index number 2, and so on. For example, our Favoritecolors attribute would be indexed like this:

Index No.

Value

1

purple

2

yellow

3

red

4

blue

To map an attribute to the first value in the collection (purple), use syntax similar to the following:

"/favoriteColor": "/Favoritecolors[1]"

To map an attribute to the third item in the collection (red), use this syntax:

"/favoriteColor": "/Favoritecolors[3]"