Claims mapping example
This is an example of a User sign-in script that reads the information provided by an external login provider and stores them on the logged on user's profile.
Use case
External login providers provide information about logged on users in the form of claims. The claims provided are highly dependent on which login-provider has been used.
This example has been developed using Google as login provider. Google provides us with 2 claims: givenname and surname. We assume that the M.UserProfile entity-definition contains 2 properties: FirstName and LastName. The script maps the values of the givenname and surname claims to these properties.
The "FirstName" and "LastName" properties do not exist out of the box. They have been added using the schema-editor.
Script
using System.Linq;
using System.Security.Claims;
if (Context.ExternalUserInfo?.Claims == null) return;
var firstName = Context.ExternalUserInfo.Claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
var lastName = Context.ExternalUserInfo.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
if (string.IsNullOrEmpty(firstName) && string.IsNullOrEmpty(lastName)) return; // No first or lastname claims found, nothing to do.
var userToUserProfile = await Context.User.GetRelationAsync<IParentToOneChildRelation>("UserToUserProfile");
if (!userToUserProfile.Child.HasValue) throw new InvalidOperationException("The logged on user has no user-profile.");
var profile = await MClient.Entities.GetAsync(userToUserProfile.Child.Value);
if (profile == null) throw new InvalidOperationException("The logged on user has no user-profile.");
if (!string.IsNullOrEmpty(firstName))
{
profile.SetPropertyValue("FirstName", firstName);
}
if (!string.IsNullOrEmpty(lastName))
{
profile.SetPropertyValue("LastName", lastName);
}
await MClient.Entities.SaveAsync(profile);
Script explanation
-
Include the libraries to be used in the script.
RequestResponseusing System.Linq; using System.Security.Claims;
-
Users can log in using either basic-authentication (username and password) or using an external login provider. If the user's been logged on using basic-authentication, Context.ExternalUserInfo will be null. In that case, there are no claims to map, so simply return from the script.
RequestResponseif (Context.ExternalUserInfo?.Claims == null) return;
-
Extract the values from the provided claims. The claims are defined as constants on the "ClaimTypes" class.
RequestResponsevar firstName = Context.ExternalUserInfo.Claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value; var lastName = Context.ExternalUserInfo.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
-
If no givenname or surname claims have been provided, there is nothing to map, so return from the script.
RequestResponseif (string.IsNullOrEmpty(firstName) && string.IsNullOrEmpty(lastName)) return;
-
The user-profile related to the logged on user needs to be loaded. The logged on user is specified in Context.User. The user-profile is associated with the User using the "UserToUserProfile" relation. It is expected for every user in the system to have an associated profile. If the user-profile cannot be found, something is wrong and we throw an exception.
RequestResponsevar userToUserProfile = await Context.User.GetRelationAsync<IParentToOneChildRelation>("UserToUserProfile"); if (!userToUserProfile.Child.HasValue) throw new InvalidOperationException("The logged on user has no user-profile."); var profile = await MClient.Entities.GetAsync(userToUserProfile.Child.Value); if (profile == null) throw new InvalidOperationException("The logged on user has no user-profile.");
-
Populate the "FirstName" and "LastName" properties of the profile entity. If one of the claims has not been provided, simply skip it.
RequestResponseif (!string.IsNullOrEmpty(firstName)) { profile.SetPropertyValue("FirstName", firstName); } if (!string.IsNullOrEmpty(lastName)) { profile.SetPropertyValue("LastName", lastName); }
-
Store the changes made to the user-profile in the database.
RequestResponseawait MClient.Entities.SaveAsync(profile);
Setup
- Create, publish and enable a User sign-in script.