AD FS 2016 and InvalidNameIDPolicy using SAML Authentication to SailPoint IdentityNow

Context

I recently had a seemingly simple task for a customer to setup a AD FS 2016 relying party trust for their SailPoint IdentityNow deployment. Sounds easy right?

In this scenario AD FS 2016 was to be the Identity Provider (IdP) and IdentityNow the Service Provider (SP). Our end-goal of the solution was to allow the customer’s users to authenticate via SAML into IdentityNow using their corporate AD DS email address and password. Great outcome from a user experience perspective and for corporate governance too!

Configuration Setup and Problem Encountered

Following SailPoint’s guide here I setup IdentityNow as a Service Provider using the Email attribute as the SAML NameID.

I then moved onto creating a new AD FS 2016 relying party trust using the sp-metadata.xml file downloaded directly from the customer’s IdentityNow portal. After some quick research of the claims required I created the following 2x AD FS Issuance Transform Rules within my new RPT:

  • Rule #1: Send LDAP Attribute (E-Mail-Addresses) as an Outgoing Claim (E-Mail Address)

  • Rule #2 Transform an Incoming Claim (E-Mail Address) to an Outgoing Claim (Name ID) with the Outgoing name ID format (Email)

Unfortunately during my testing I was continually returned the following web page message from the customer’s IdentityNow portal. 

This was occurring after the initial AD FS authentication and token being issued.

Whilst the web page error is vague in it’s description of the error, I knew that because the initial AD FS authentication had succeeded that I was dealing with a claims issue between the IdP and SP. 

InvalidNameIDPolicy SAML Response

Diving into the SAML response using Fiddler and a SAML decoder I could see a SAML status code of “InvalidNameIDPolicy“. Problem discovered! The most useful and easily accessible diagnostic information was actually straight out of the AD FS server’s local event viewer logs under Applications and Services Logs > ADFS > Admin (in hindsight I should have looked here first!).

Events #364 and #321 also verified that the NameIDPolicy required from IdentityNow was not being met by the AD FS token issued.

Event ID #364
Encountered error during federation passive request. 
The SAML request contained a NameIDPolicy that was not satisfied by the issued token.

Event ID #321
The SAML authentication request had a NameID Policy that could not be satisfied. 

Tip: If you encounter the same problem I had, have a look at the detail of these two events and compare the Requested NameIDPolicy versus the Actual NameIDPolicy to discover what exactly is missing from the AD FS token.

Sending SPNameQualifier as a Claim

The resolution to this problem for me was to ensure that an SPNameQualifier value was sent as a claim property from AD FS to IdentityNow.

As far as I know, this is an undocumented requirement to have SAML authentication tokens from AD FS 2016 accepted by SailPoint IdentityNow.

The SPNameQualifier value needed to match the Entity ID specified in our IdentityNow portal under Admin > Global > Security Settings > Service Provider.

Because I couldn’t find SPNameQualifier property in any of the Claim rule templates I used a Custom Rule which you can create as shown below.

The following Claim rule combines my original Rule #2 (described at the beginning of this post) with the new claim property for SPNameQualifier.

Note: If using the below claim code remember to replace “insertValueHere” with your Entity ID specified in IdentityNow.

After updating my claim rule with the above change a quick test of authenticating to IdentityNow via AD FS SAML was successful and I could also finally see SAML authentication events from the IdentityNow Activity Tab.

Happy days! 🙂

Summary

In conclusion when configuring SAML authentication via AD FS 2016 (IdP) to IdentityNow (SP) you may need to insert a SPNameQualifier value as an outgoing claim property from AD FS. The SPNameQualifier value should match the Entity ID value specified in your IdentityNow portal.

Cheers, Jesse

ADFS Metadata Conversion for Shibboleth

I recently blogged about the issues integrating Shibboleth Service Providers with ADFS. As an update to that blog one of Kloud’s super smart developers (Alexey Shcherbak) has re-written the FEMMA ADFS2Fed.py Python script in PowerShell, removing the need for Python and the LXML library! The ADFS2Fed converts ADFS metadata for consumption by a Shibboleth SP. Below is the output of Alexey’s labour, awesome work Alexey!

[code language=”PowerShell” gutter=”false”]
$idpUrl = "https://federation.contoso.com";
$scope = "contoso.com";
$filename = ((Split-Path -parent $PSCommandPath) +"\federationmetadata.xml");

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq");
$xel = [System.Xml.Linq.XElement]::Load($filename);

$shibNS = New-Object System.Xml.Linq.XAttribute @(([System.Xml.Linq.XNamespace]::Xmlns + "shibmd"), "urn:mace:shibboleth:metadata:1.0");
$xel.Add($shibNS);

$scopeContent = New-Object System.Xml.Linq.XElement @("{urn:mace:shibboleth:metadata:1.0}Scope", (New-Object System.Xml.Linq.XAttribute @("regexp","false")),$scope);
$scope = New-Object System.Xml.Linq.XElement @("{urn:oasis:names:tc:SAML:2.0:metadata}Extensions",$scopeContent);
$xel.AddFirst($scope);

$authN = New-Object System.Xml.Linq.XElement @("{urn:oasis:names:tc:SAML:2.0:metadata}SingleSignOnService", (New-Object System.Xml.Linq.XAttribute @("Binding","urn:mace:shibboleth:1.0:profiles:AuthnRequest")), (New-Object System.Xml.Linq.XAttribute @("Location", ($idpUrl+"/adfs/ls/"))) );
$firstSSO = [System.Linq.Enumerable]::First( $xel.Descendants("{urn:oasis:names:tc:SAML:2.0:metadata}SingleSignOnService"));
$firstSSO.AddBeforeSelf($authN);

$xel.Elements("{http://www.w3.org/2000/09/xmldsig#}Signature")|%{ $_.Remove()};
$xel.Elements("{urn:oasis:names:tc:SAML:2.0:metadata}RoleDescriptor") | %{$_.Remove()};
$xel.Elements("{urn:oasis:names:tc:SAML:2.0:metadata}RoleDescriptor") | %{$_.Remove()};
$xel.Elements("{urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor")| %{$_.Remove()};

$xel.Save(($filename+"ForShibboleth.xml"), [System.Xml.Linq.SaveOptions]::None)
[/code]

Shibboleth Service Provider Integration with ADFS

If you’ve ever attempted to integrate a Shibboleth Service Provider (Relying Party) application with ADFS, you’d have quickly realised that Shibboleth and ADFS are quite different beasts. This blog covers off some of the key issues involved and provides details on how to get ADFS to play nice with a Shibby Service Provider (SP). This blog does not cover configuring ADFS to participate as a member in a Shibboleth Federation like InCommon or the Australian Access Federation (AAF). That type of integration presents a different set of challenges, contact us to discuss your needs.

Before we get to the details, some terminology varies between the two federation services, the table below lists the key differences. Shibboleth terminology will be used throughout this blog.

AD FS Name Shibboleth Name
Security tokenAssertion
Claims providerIdentity provider (IdP)
Relying partyService provider (SP)
ClaimsAssertion attributes

Below are the key issues involved with integration of Shibboleth SPs with ADFS. Each will be expanded upon later in this blog.

1. Metadata incompatibility.

2. Incorrect SAML Name Format in assertions.

3. Missing Assertion Attributes.

1. Metadata Incompatibility

ADFS generates publishes its metadata https://<FederationServiceName>/FederationMetadata/2007-06/FederationMetadata.xml. There is no functionality to modify what is published. When a Shibboleth SP consumes ADFS metadata the following issues can arise:

1. ADFS Metadata contains information pertaining to WS.Trust and WS.Federation. This is not consumed by Shibboleth SPs.

2. Shibboleth Scope information is not generated by ADFS.

3. Shibboleth XML Name Space information is missing.

The easiest method to address these issues is to pre-process the metadata for consumption by the Shibboleth SP. For this, the Federation Metadata Manager (FEMMA) toolset is useful. This toolset was developed to assist in configuring ADFS to participate as a member in a Shibboleth Federation (e.g InCommon or Australian Access Federation). Whilst this is overkill for integration of a Shibby SP with ADFS, also included within this toolset is the ADFS2Fed.py Python script with reads ADFS metadata and corrects the above issues. Run it, configure the Shibboleth SP to retrieve IdP metadata from a local file, job done!

2. Incorrect SAML Name Format

When ADFS issues assertions configured using the standard ADFS Claims Rules interface it uses the name format urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified. Shibboleth expects urn:oasis:names:tc:SAML:2.0:attrname-format:uri. This issue unfortunately means that assertions will need to be issued by custom Claim Rules.

To apply the correct SAML Name Format to an assertion attribute from an ADFS attribute store, a two stage process is needed:

1. Retrieve the assertion attribute from the attribute store and store as an incoming assertion. For example, the custom ADFS Claims Rule below queries Active Directory for the authenticating user’s User Principal Name and stores the value into the incoming assertion with type of http://kloud/internal/userprincipalname:

c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"] => add(store = "Active Directory", types = ("http://kloud/internal/userprincipalname"), query = ";userPrincipalName;{0}", param = c.Value);

2. Issue a new assertion of the required type using the incoming assertion value. The following custom ADFS Claims Rule retrieves the incoming assertion of type http://Kloud/internal/userprincipalname, re-issues it as type urn:oid:1.3.6.1.4.1.5923.1.1.1.6, and assigns a name format of urn:oasis:names:tc:SAML:2.0:attrname-format:uri.

c:[Type == "http://kloud/internal/userprincipalname"] => issue(Type = "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", Value = c.Value, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/attributename"] = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");

3. Missing Assertion Attributes

By default, a Shibboleth SP expects assertions from the eduPerson class. Some of these have specific requirements, below are the troublesome ones and sample ADFS custom Claim Rules to get you going. Note – Scoped attributes must have a scope matching the scope provided in the IdP metadata, or by default the Shibboleth SP will drop them. If using the FEMMA ADFS2Fed.py script, the Shibboleth Scope is entered as a parameter.

1. eduPersonTargetedID (urn:oid:1.3.6.1.4.1.5923.1.1.1.10)

This assertion attribute is derived from SAML NameID. It is required to identify the Identity Provider, Service Provider and provide a consistent and obfuscated identifier for the user. Obfuscation of the user identifier ensures that whilst the user can be tracked across services, they cannot be identified directly to a named account.

To construct this first we grab an immutable identifier for the user – the users Active Directory Security Identifier (SID) is ideal as it is constant for the life of the account unlike Windows Account Name (sAMAccountName) which can change. We then use the ppid function to encrypt the SID using the federation service name of ADFS as a seed. Finally we store this value as an incoming assertion type of http://kloud/internal/persistentid.

 c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid"] => add(store = "_OpaqueIdStore", types = ("http://kloud/internal/persistentId"), query = "{0};{1};{2}", param = "ppid", param = c.Value, param = c.OriginalIssuer);

Next we the provide the IdP and SP name qualifiers (EntityIDs) as static strings and issue the assertion as the required type – urn:oid:1.3.6.1.4.1.5923.1.1.1.10:

 c:[Type == "http://kloud/internal/persistentId"] => issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oid:1.3.6.1.4.1.5923.1.1.1.10", Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/spnamequalifier"] = "<SP Entity ID>", Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/namequalifier"] = "<IdP Entity ID>");

2. eduPersonUniqueId (urn:oid:1.3.6.1.4.1.5923.1.1.1.13)

Similarly to eduPersonTargetID, this Assertion Attribute is another obfuscated user identifier. It is scoped, therefore it is comprised of a unchanging unique identifier of the user concatenated with their domain e.g. <identifier>@kloud.com.au. Examples given in the eduPerson schema reference show a GUID as the user identifier. The Active Directory GUID fits requirements, however performing a query against Active Directory for the GUID value as shown will not result in a correctly formatted GUID. This is due to the conversion of the GUID binary value from Active Directory.

Recommended is either to implement a String Processing Store, or populate an attribute store with a GUID converted to a correctly formatted string. Once the GUID value is converted and stored in the incoming assertions pipeline is can be concatenated with the Scope value and assigned the correct name format, as shown below:

c:[Type == "http://kloud/internal/objectguid"] => issue(Type = "urn:oid:1.3.6.1.4.1.5923.1.1.1.13", Value = c.Value + "@kloud.com.au", Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/attributename"] = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");

3. eduPersonPrincipalName (urn:oid:1.3.6.1.4.1.5923.1.1.1.6)

This assertion attribute provides a non-obfuscated, scoped user identifier. Active Directory provides a User Principal Name value which is suitable for this purpose. It can be queried and stored into the incoming assertion pipeline like so:

c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"] => add(store = "Active Directory", types = ("http://kloud/internal/userprincipalname"), query = ";userPrincipalName;{0}", param = c.Value);

And issued in with the correct type and name format like this:

c:[Type == "http://kloud/internal/userprincipalname"] => issue(Type = "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", Value = c.Value, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/attributename"] = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");

4. eduPersonAffiliation (urn:oid:1.3.6.1.4.1.5923.1.1.1.1)

This attribute is used to categorise user accounts for the purpose of assigning access privileges. Refer to the eduPerson schema reference for a definition of affiliations. Methods to assign affiliation values vary between organisations, however in regards to issuing an assertion the recommended approach would be to populate an attribute either in Active Directory or another attribute store. Below is an example where the Active Directory attribute Employee Type (employeeType) stores the affiliation:

c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"] => add(store = "Active Directory", types = ("http://kloud/internal/affiliation"), query = ";employeeType;{0}", param = c.Value);

And issued in with the correct type and name format like this:

c:[Type == "http://kloud/internal/affiliation"] => issue(Type = "urn:oid:1.3.6.1.4.1.5923.1.1.1.1", Value = c.Value, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/attributename"] = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");

To issue eduPersonScopedAffiliation (urn:oid:1.3.6.1.4.1.5923.1.1.1.9) issue the incoming assertion concatenated with the scope as below:

c:[Type == "http://kloud/internal/affiliation"] => issue(Type = "urn:oid:1.3.6.1.4.1.5923.1.1.1.9", Value = c.Value + "@kloud.com.au", Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/attributename"] = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");

 

5. eduPersonAssurance (urn:oid:1.3.6.1.4.1.5923.1.1.1.11)

The assertion provides information on the levels of assurance involved in the authentication of a user, both in the proof required for account creation and the strength of authentication mechanism/s used. The eduPersonAssurance assertion is a text string, e,g. “urn:mace:aaf.edu.au:iap:id:1” indicates an Identity Assurance Level of 1, whilst “urn:mace:aaf.edu.au:iap:authn:1” shows an Authentication Assurance Level of 1. In most instances this assertion can be issued as a simple value assertion as below:

=> issue(Type = "urn:oid:1.3.6.1.4.1.5923.1.1.1.11", Value = "urn:mace:aaf.edu.au:iap:id:1", Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/attributename"] = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri");

 

Hopefully this blog helps you with your Shibboleth integration issues. In addition, federation services should be implemented with alignment to a larger identity and access management strategy. If you need assistance call us and we’ll sort it out for you!

Follow Us!

Kloud Solutions Blog - Follow Us!