ADFS: Claim rule to issue recursive group membership of a user

In the context of Active Directory Federation Services, the Relying Party Trust configuration implies Issuance Transform Rules, in which miscellaneous info is issued from a user to the application, most of the time the usual SAMAccountName, UPN, Name/Surname, Email Adresses etc.

We’ll consider here the case of a very specific request, but not that uncommon.

User X is a member of an AD Group called “Team_IT“, and this group is a member, among others, of groups “ADMGRP_MyApp” and “ADMGRP_AD“.   We want to issue this recursive membership (i.e. to ADMGRP’s) and, cherry on top, filter the issued group list because Team_IT group is a member of dozens of other groups and we only want the ones beginning with ADMGRP.

There is a direct way to send Group Membership as Claim but it’s kinda crappy. If I understood it well, it only allows you to check ONE group membership, and issue a custom value in the claim type of your choice.  e.g. User X is a member of group Teams_IT, then issue “HelloWorld” in claim type “Name ID”:

No recursive check, no multiple group check. Well you can make one rule per -hardcoded- group; hello flexibility! …

So we are left with custom rules, and we must dive into the fantastic world of ADFS Rule language.  I haven’t understood it all, but know these rules already:

1. The claim type is *whatever you want*.  All drop-down menu entries giving you a claim type, translate this into a line of “rule language” with a link to a non-existent parameter definition.


This link http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname doesn’t actually exist (well, OK, not a 404 because the site itself exists but hey..). You could really give any reference there. But of course when using the claims, you have to give that precise reference.

2. If you use a custom LDAP query, it must be in the form

QUERY = “<QUERY_FILTER>;<ATTRIBUTES>;<DOMAIN_NAME>\<USERNAME>”

where the query filter is not mandatory (default value is current samaccountname); the attributes are those returned and the username is the one used to read the AD (pretty much any authenticated user can).

I quickly found how to return the attribute “memberOf”, but as you may well know this attribute only contains the direct group membership.

There is also a mysterious filter called LDAP_MATCHING_RULE_IN_CHAIN but which only works on a DN (DistinguishedName) object, which is not a value often used in our ADFS context.

So in short we’ll have to create 3 rules to achieve what we want.

In your ADFS rule add wizard : choose “Send Claims as Custom Rule” in the dropdown menu, and be sure to give them this order (rules are processed chronologically):

1. Store the username as distinguishedName (DN)

We store the SAMAccountName into a custom claim type that I named “nameDN” (I totally made up the link from the common “name” type).  Note the query like explained above, which has an empty first parameter (thus the default SAMAccountName, that we want), takes its attribute distinguishedName, and then uses the same Samaccountname again to access AD.

2. Store all group membership recursively, using the syntax with the DN

This is tricky. the chain of numbers is the LDAP_MATCHING_RULE_IN_CHAIN mentioned above. We use 2 parameters also, one still being the SAMAccountname and the other being the custom type DN that we made on the previous rule.
The claims/group is also made up, from the one I found in the example for the next  rule.

3. Issue the filtered list of group following our needs

Using the made up Group type claim, and filtering its name to begin with ADMGRP.

There, both ADMGRP_* groups are correctly issued!

Rule syntax is a merge from MSFT Pierre Audonnet (merci !) and various users on Technet (thanks!)

All sources:

https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/create-a-rule-to-send-group-membership-as-a-claim
https://social.technet.microsoft.com/wiki/contents/articles/8008.ad-fs-2-0-selectively-send-group-membership-s-as-a-claim.aspx
https://social.technet.microsoft.com/Forums/ie/en-US/f238d2b0-a1d7-48e8-8a60-542e7ccfa2e8/recursive-retrieval-of-all-ad-group-memberships-of-a-user?forum=ITCG
https://blogs.msdn.microsoft.com/pinch-perfect/2015/09/14/querying-attributes-from-active-directory-using-adfs-with-a-3rd-party-identity-provider/
https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/ff608234(v=ws.10)#code-snippet-5
https://social.technet.microsoft.com/Forums/lync/en-US/ca566e15-4b3b-4830-ae65-e25d83251c07/adfs-claim-to-flatten-groups-and-return-full-dn?forum=winserverDS

8 thoughts on “ADFS: Claim rule to issue recursive group membership of a user

  1. Hello! Can you help me)

    I have claim rules:

    #For UPN
    c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname”, Issuer == “AD AUTHORITY”]
    => issue(store = “Active Directory”, types = (“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”), query = “;userPrincipalName;{0}”, param = c.Value);

    c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”]
    => issue(claim = c);

    #For Group
    c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname”, Issuer == “AD AUTHORITY”]
    => issue(store = “Active Directory”, types = (“http://schemas.xmlsoap.org/claims/Group”), query = “;tokenGroups(domainQualifiedName);{0}”, param = c.Value);

    c:[Type == “http://schemas.xmlsoap.org/claims/Group”]
    => issue(claim = c);

    How can i get nested group for UPN? is it really possible?

     

    1. What do you mean exactly with “nested group for UPN”?

      If I understand your rules correctly, you are casting the WindowsAccountName into the UPN, why is that?
      Just follow my article here, and use the SAMAccountName as is, to look for group membership, and don’t forget the LDAP_MATCHING_RULE_IN_CHAIN value that I mention, if you want it to be resursive.

      And if you need the UPN also, just create a separate rule for it.

  2. Hello Bix,

    thank you for this useful Tutorial.

    We use your solution and it works  so far. The only problem we have, on the 2nd. Rule it takes 8secs. to be done.

    Do you have maybe and idea how to fix this?

     

    1. Greetings,
      This is because ALL groups are returned, and we know by experience that everything group-membership related is friggin’ slow in AD (noticed the poor perfs of the Get-ADGroupMember cmdlet in PowerShell?). The only solution I’d see is to reduce the recursivity of some groups, maybe review your nested groups structure.

  3. what are the names for the rule1, rule2? I am new to AD FS and trying to see if your article helps me to get full list groups for a user in one claim.

    1. Greetings,
      For some reason your comment from 1.5y ago got stuck in spam filter and I’ve noticed it only now 😀

      Names for rules do not matter, they are a pure label, you can use whatever name you want.

      If you follow my article above you will have full list of groups, including recursive ones, in one claim : “Group”.

  4. Thank you for the article, if I wanted to return the groups not as arrays but separated by a comma how could I do it?

    1. Greetings,
      For some reason your comment from 1y ago got stuck in spam filter and I’ve noticed it only now 😀

      By default the Groups are returned in an array in 1 claim. I suppose there might be a way to transform this but my knowlegde of ADFS doesn’t go that far, I mean if it ever exists – sorry !

      Most of the time, when we need to operate changes on claims, we do it on the final product receiving the claims, instead of ADFS itself. I feel like possibilities are limited within ADFS itself.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.