A lot of the work I do daily is around Security, both On-premises and within the Cloud services such as Microsoft 365. I remember many moons ago working on a Military project for Microsoft Consulting Services (MCS) in the UK and working with Active Directory Federated Services (ADFS) before it was a product. Each day, I would test this new super-alpha internal project with no name, provide feedback, and then get a new build. The new build consisted of a few DLLs and a batch script to install. It was not as easy to install as it is now. Those were the days when many products we now take for granted didn’t exist as they do now as they were dynamically building them as part of larger projects with clients.

Anyway, I digress from what I want to talk about. Many of my clients utilize either pure SAML or WS-Fed SAML to create a single-sign-on solution for their on-premises applications or cloud services. A few recent ones use SharePoint or other applications on-premises connected to Active Directory Federated Services (ADFS), which connects to other platforms such as Ping Federate or Oracle Access Manager. The logic here is that they need to support multiple authentication options but transform everything so that SharePoint or the other applications understand what to do with the generated SAML. Active Directory Federated Services (ADFS) works well as the go-between in this scenario. The different authentication platform connects to ADFS as a claims provider passing its claims through to ADFS and onto the application, such as SharePoint. From a relying party perspective, SharePoint only needs to understand ADFS, not the other authentication solution.

To pass the claims from one authentication platform onto ADFS and then into the application, you often need to transform the claims to match what is required. 

During some specific work, I had to create multiple claims transformations to remap claim schemas together so SharePoint could utilize them. The original claims were from a different authentication platform that did not use the same schemas. An example was the email address. The schema was this in the original SAML from the other authentication platform: “http://schemas.microsoft.com/ws/2008/06/identity/claims” and the claim attribute “email.” 

SharePoint, however, required this schema “http://schemas.xmlsoap.org/ws/2005/05/identity/claims” with the claim attribute being “emailaddress.”

As you can see, a classic claims transformation is required to ensure SharePoint can see the attribute values for either authorization into SharePoint or as extra property values if you are using code. The user interface within Active Directory Federated Services (ADFS) does allow for this type of mapping by creating a “Transformation Rule.”

These rules require a few pieces to work:

  • Incoming claim type and format
  • Outgoing Claim type and format
  • Claim value configuration

In simple words, the syntax says: If the specified claim exists, get that value, then add the new claims type and populate that with the current value from the retrieved claim type.

You can then expect a transformation rule to look very similar to this:

c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/email"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

When creating the rules within ADFS, you can use the wizard for the rule creation or create a custom claim rule and type the syntax similar to the above.

Another approach is using PowerShell to create the rules, allowing quick and straightforward execution. The PowerShell command to use is either “Set-ADFSRelyingPartyTrust” if you need to transform at the relying party level or “Set-AdfsClaimsProviderTrust” to transform at the authentication platform level. Each command utilizes a property for storing any transform rules; for a Relying Party, it is called “IssuanceTransformRules“; for a Claims Provider Trust, it’s called “AcceptanceTransformRules.”

To format these properties, it is as simple as creating a PowerShell variable and combining the syntax we looked at earlier plus the name we wish to call the rule together, as shown below.

$transformrule = @'

@RuleName = "Transform Email to Emailaddress" 
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/email"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

'@

We need to load this information into the required command to create the transform rules. For example, to use this transform rule with a claims provider trust, we would execute the following:

$claimprovidertrust = Get-AdfsClaimsProviderTrust -Name "ClaimsProvider"

$transformrule = @'

@RuleName = "Transform Email to Emailaddress" 
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/email"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

'@

Set-AdfsClaimsProviderTrust `
    -TargetName $claimprovidertrust.Name `
    -AcceptanceTransformRules $transformrule

The warning here is that it will remove whatever rules exist when executing these commands. To ensure any existing rules do not delete, you first need to retrieve the list of current rules, add them into a new “Rule Set,” then use that as the rules when setting the “Claims Provider Trust” or “Relying Party” configuration.

$claimprovidertrust = Get-AdfsClaimsProviderTrust -Name "ClaimsProvider"

$transformrules = New-AdfsClaimRuleSet -ClaimRule @'

@RuleName = "Transform Email to Emailaddress" 
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/email"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

@RuleName = "Issue UPN and Windows Account Name from Querying Mail" 
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/email"]
=> issue(store = "Active Directory", types = ("https://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", "https://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"), 
         query = "mail={0};userPrincipalName,sAMAccountName;{1}", param = c.Value, param = "DOMAIN\ServiceAccount");

'@

Set-AdfsClaimsProviderTrust `
    -TargetName $claimprovidertrust.Name `
    -AcceptanceTransformRules $transformrules.ClaimRulesString

To use the claims within a “Relying Party,” the PowerShell needs changing to the following:

$relyingparty = Get-ADFSRelyingPartyTrust -Name "RelyingParty"

$issuancerules = New-AdfsClaimRuleSet -ClaimRule @'

@RuleName = "Transform Email to Emailaddress" 
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/email"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

@RuleName = "Issue UPN and Windows Account Name from Querying Mail" 
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/email"]
=> issue(store = "Active Directory", types = ("https://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", "https://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"), 
         query = "mail={0};userPrincipalName,sAMAccountName;{1}", param = c.Value, param = "DOMAIN\ServiceAccount");

'@

Set-ADFSRelyingPartyTrust `
    -TargetName $relyingparty.Name `
    -IssuanceTransformRules $issuancerules.ClaimRulesString

Adding transformation rules may look complicated but once done, you can create a reusable PowerShell script of even a file that contains them all and use them again and again.