When SAS Viya is configured for OpenID Connect authentication with Azure Active Directory an important decision is how the end-users should be identified. The documentation suggests using email or upn, while previously we have discussed using the objectID. In this post I want to explore using different attributes of the Azure AD users to identify them to SAS Viya. Here, we will specifically be looking at adding the different attributes as claims to the ID Token used to authenticate the users to SAS Logon Manager. In a future post we will explore making the same attributes available to the Identities microservice using SCIM.
First and foremost, why would you want to use something other than email address, user principal name, or objectID as the unique identifier for your end-users? If you are doing everything within Microsoft Azure; you are running SAS Viya, you are accessing data sources such as Azure SQL Server, utilising ADLS, accessing file in Azure storage accounts, etc. then there really is no need to look at changing the unique identifier for your end-users. Either of the three attributes; email, upn, or objectID, will be sufficient. However, if you need to access a mixture of cloud and on-prem resources you might want to consider using a different attribute to identify your end-users. For example, if you want to use Kerberos Hybrid authentication with Protocol Transition, you’ll need to ensure the unique identifier matches the Kerberos User Principal Name. Quite often the Kerberos User Principal Name will not match the Azure Active Directory user principal name.
In many cases your organization will be synchronizing on-premises users into Azure Active Directory. It will be quite rare that users exist only in Azure Active Directory and not in an on-prem domain services instance. When Azure AD Connect is used to synchronize users from on-premises to Azure Active Directory we gain a series of on-prem user attributes. Shown here are the standard on-premises attributes for a user:
Select any image to see a larger version.
Mobile users: To view the images, select the "Full" version at the bottom of the page.
In my example Azure AD tenant the Azure AD user principal name is {{user}}@gellabtest.onmicrosoft.com whereas from the screenshot above we can see the on-premises user principal name is {{user}}@GELLAB.NET. So, if I wanted to leverage Kerberos Hybrid authentication with Protocol Transition, I would need to use the on-premises UPN rather than the Azure AD UPN since only the on-premises UPN would match my Kerberos UPN. Or perhaps I need to leverage the on-premises SAM account name and will rely on the Kerberos default Realm to enable Kerberos Hybrid authentication with Protocol Transition.
In addition to these attributes, Azure AD Connect enables you to extend the schema in Azure Active Directory with your own attributes from on-premises Active Directo....
Within Azure Active Directory you can extend the attributes included in the OIDC tokens and SAML tokens using Claims Mapping Policies. The claimsMappingPolicy resource type is described in the Microsoft Graph documentation. This represents the claim-mapping policies for WS-Fed, SAML, OAuth 2.0, and OpenID Connect protocols, for tokens issued to a specific application. You can use claims-mapping policies to:
A new Claims Mapping Policy can be created in your Azure Active Directory Tenant. Then once the Claims Mapping Policy is defined this can be applied to the Service Principal associated with the App Registration providing OIDC for you SAS Viya environment. The attributes that be used in the Claims Mapping Policy are given in the Microsoft Graph documentation in this table. For our aim to make the on-premises UPN available in the ID Token we should take note of the additional information under the table in the Microsoft Graph documentation. Specifically, the following statement:
onPremisesUserPrincipalName attribute: When using an Alternate ID, the on-premises attribute userPrincipalName is synchronized with the Azure AD attribute onPremisesUserPrincipalName. This attribute is only available when Alternate ID is configured but is also available through MS Graph Beta: https ://graph.microsoft.com/beta/me/ .
This means that we need to configure an Alternate ID to be able to leverage the on-premises UPN in the ID Token, otherwise the value will simply not be included in the token. I did experiment with trying to transform some of the other on-premises attributes within the Claims Mapping Policy to construct the on-premises UPN; but the transformation in the Claims Mapping Policy cannot accept another transformation as an input. As such, there was no-way to change the case of the on-premises domain name and join that to the on-premises SAM account name to construct a <user>@<REALM> type of attribute.
Microsoft supports using email as an alternate login ID as a public preview feature as documented here. This public preview feature is easy to enable, as a global administrator in Azure AD, you just select Email as alternate login ID from the Azure AD Connect settings as shown here:
Then on the following screen check the box and save the settings. With the policy applied, it can take up to 1 hour to propagate and for users to be able to sign in using their alternate login ID. This can also be configured using PowerShell as covered in the documentation. This does not force your end-users to authenticate with the email, they can still authenticate with the Azure Active Directory UPN as normal. Otherwise, an alternate login ID will need to be specified in the Azure Active Directory Connect settings as covered in the Microsoft documentation here. So, if you want to be able to add the on-premises user principal name to your ID Token you will need to check how your organization has setup the Azure AD Connect.
Creating the Claims Mapping Policy is quite simple. We can use the Microsoft Graph Explorer to submit the request. The user performing this will require the Policy.ReadWrite.ApplicationConfiguration permission. We make a POST request to https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies with the following content:
{
"definition": [
"{\"ClaimsMappingPolicy\":{\"Version\":1,\"IncludeBasicClaimSet\":\"true\", \"ClaimsSchema\": [{\"Source\":\"user\",\"ID\":\"onPremisesUserPrincipalName\",\"JwtClaimType\":\"onPremisesUserPrincipalName\"}]}}"
],
"displayName": "OnPremUPN_Custom_Claims"
}
This policy defines that a value will be added to the OIDC tokens since the schema only includes a JwtClaimType. In the OIDC token the claim will have the name given by the JwtClaimType, so in our case "onPremisesUserPrincipalName". The value of this claim will be from the "user" source and the attribute "onPremisesUserPrincipalName". Which will look like the following in the Microsoft Graph Explorer:
We need to take note of the returned ID value as we will need that to apply our new Claims Mapping Policy. We can also use the ID and change the request to PATCH if we need to update the policy. Should you forget to take note of the ID you can make a GET request to https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies and it will return all the Claims Mapping Policies in your Azure AD tenant. This Microsoft Graph endpoint also supports $filter, so if there are a lot of results you could make a GET request of https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies?$filter=displayName eq 'OnPremUPN_Custom_Claims' to be able to get the ID value again.
Once you have created the Claims Mapping Policy, you can apply it to the Service Principal associated with your App registration for OIDC. I would recommend first creating a test App registration so that you can confirm the values in the token before you make the changes to the actual App registration for your SAS Viya environment. You will need to know the ID of the Service Principal. From the Azure Portal when reviewing your App registration on the Overview page:
Select the link against "Managed application in local directory" to view the Service Principal; this takes you to the corresponding Enterprise Application. On the following screen the Object ID is the value that is required:
To be able to assign the Claims Mapping Policy to the Service Principal requires the following permissions: "Policy.Read.All and Application.ReadWrite.All, Policy.ReadWrite.ApplicationConfiguration and Application.ReadWrite.All", as covered in the Microsoft documentation. Again, we can use the Microsoft Graph Explorer to apply the Claims Mapping Policy. We make a POST request to https://graph.microsoft.com/v1.0/servicePrincipals/{id}/claimsMappingPolicies/$ref , where {id} is the ID of our Service Principal. The body of the request contains the reference to the Claims Mapping Policy object using an @odata.id property:
{
"@odata.id":"https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies/{claimsMappingPoliyID}"
}
The response to the POST request will not contain any content. You can confirm the Claims Mapping Policy has been applied by making a GET request to https://graph.microsoft.com/v1.0/servicePrincipals/{id}/claimsMappingPolicies/ , where {id} is the ID of our Service Principal.
Just applying the Claims Mapping Policy is not the final step to make the additional claim available to our application. You must also look at the security considerations documented by Microsoft. The principle is that applications that receive tokens rely on the fact that the claim values are authoritatively issued by Azure AD and cannot be tampered with. However, when you modify the token contents through claims-mapping policies, these assumptions may no longer be correct. Applications must explicitly acknowledge that tokens have been modified by the creator of the claims-mapping policy to protect themselves from claims-mapping policies created by malicious actors. This can be done in one the following ways:
Without this, Azure AD will return an AADSTS50146 error code.
For single tenant apps, you can set the acceptMappedClaims property to true in the application manifest. As documented on the apiApplication resource type, this allows an application to use claims mapping without specifying a custom signing key. This does require the requested token audience to use a verified domain name of your Azure AD tenant, which means you should ensure to set the Application ID URI (represented by the identifierUris in the application manifest) for example to https://contoso.com/my-api or (simply using the default tenant name) https://contoso.onmicrosoft.com/my-api .
This is the approach I have taken with the testing application:
With this complete the application will now include our additional claims in the ID Token.
Rather that diving straight into configuring SAS Viya to use the additional claim we should validate the contents of the token first. Hence, why we have applied the Claims Mapping Policy to a test application first. We can follow the steps in this blog: https://s4erka.wordpress.com/2021/03/26/azure-ad-application-to-test-oauth2-0/ to validate the contents of the token. We need to complete the following steps in our test application:
These can both be set on the Authentication page of our test application in the Azure Portal.
Then we can construct a URL to access our test application based on the following template (should be one line, broke into lines here to readability):
https://login.microsoftonline.com/{{TENANT_ID}}/oauth2/v2.0/authorize
?client_id={{APP_ID}}&nonce=defaultNonce
&redirect_uri=https%3A%2F%2Fjwt.ms
&scope=openid+profile+User.read+offline_access
&response_type=id_token
For example:
https://login.microsoftonline.com/######-53d9-44b7-8e88-#########/oauth2/v2.0/authorize?client_id=########-25db-4891-9a1d-10f2fe######&nonce=defaultNonce&redirect_uri=https%3A%2F%2Fjwt.ms&scope=openid+profile+User.read+offline_access&response_type=id_token
Browsing to this URL and authenticating as one of our users in Azure Active Directory will redirect us to JWT.ms with our token:
As we can see our on-premises User Principal Name from the Claims Mapping Policy is now included in the ID Token.
This means that we can apply this Claims Mapping Policy to the App registration we are using for ODIC authentication with SAS Viya. In the SAS Viya configuration we need to ensure the attributeMappings.user_name configuration field now uses onPremisesUserPrincipalName. Also, within SAS Viya you need to ensure that the Identities microservice is also using UPN as the accountId property, so that SAS Logon Manager can correctly query the group membership information for our users when they login. In the next post we’ll look at how we can push the on-premises User Principal Name via SCIM to ensure Identities is also using this value to identify our end-users. With both changes in place our SAS Viya environment will now leverage our on-premises User Principal Name to authenticate and identify our end-users.
Find more articles from SAS Global Enablement and Learning here.
Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.
Register today!Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning and boost your career prospects.