Map custom provider 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.
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. Currently you can map the following attributes when creating a custom provider:
- /displayName. User's full name.
- /email. User's email address,
- /verifiedEmail. Date and time that the user's email address was verified.
- /name/familyName. User's last name.
- /name/givenName. User's first name.
- /photo. URL that points to a photo of the user.
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 listed first:
{
"attribute_map": {
"/email": "/email",
"/name/givenName": "/Firstname",
"/name/familyName": "/Lastname"
}
}
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",
"/displayName": "/Fullname"
}
}
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.
Map 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 listed first:
{
"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 (right) are mapped to their Identity Cloud user profile counterparts (left):
{
"attribute_map": {
"/name/giivenName": "/first_name",
"/name/familyName": "/last_name",
"/email": "/email_address",
"/emailVerified": "/email_verified"
}
}
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 /name/givenName and /name/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:
"/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:
"/name/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:
"/name/givenName": "/given~1name"
This is another construction you’re unlikely to run into. But you never know.
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 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 <saml:AttributeStatement> section:
<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",
"/name/givenName": "/Firstname",
"/name/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:AttributeStatementelement. That has two important implications:
-
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.)
-
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.
Updated over 1 year ago