Recently, I worked on an interesting scenario where we needed to block access for certain Relying parties from internet. Do note there is exactly a similar article to do this for O365. You can read about it more here. However, the x-ms-forwarded-client-ip header is only inserted by Exchange online. We are dealing with a non-O365 app. I started researching about it and came across few interesting articles. Before I came across those articles I spend a lot of time on this article and realized it wont be so easy to setup the custom claim rule. I was wrong. Its actually not that difficult for the scenario mentioned below.
This was the requirement that was asked for the setup.
a) Block all internet access.
b) Allow only a few users who are member of a certain group to access from internet.
c) Internally everyone should be able to access the application.
If this was an ADFS on Windows Server 2016 this would be relatively easier to do using Access Control Policies with no skills required to build custom rules.
However, I had an ADFS3.0 on Windows Server 2012 infra to deal with; which requires custom claim rules for this scenario.
Before I go in the nuances of how to do it; let me share a brief on the process of claims pipeline and how it works. Some common terms easily available and readable on the internet.
A Relying Party is pretty much nothing more than an application (in our case any SAML aware application) that you want to send claims to authenticate users.
A claim provider is usually the Active Directory that stores the attributes needed for authentication. In some cases it can also be another Identity provider, for example an SAML 2.0 Identity provider, which sends an SAML response to AD FS.
A claim, however, is an attribute that can identify an identity. For example, the Active Directory attribute User-Principal-Name (UPN) or Group SID.
The claims pipeline is defined in this article.
Before the claims are handed over to the relying party trusts; they go through a claims pipeline where they are modified; converted; authorized or simply passed on to the relying party.
To achieve the above requirement we faced a few challenges.
a) Internal users must not be affected. Access to the app must work normally. Issuance Authorization Rule must allow authorization for all
b) All external users must be blocked except those who are members of the active directory group. Issuance Authorization rule must detect or understand the network and where the user requests are coming from and take necessary action.
Here is the typical setup.
Here the issuance authorization rule we wrote was as below.
b) Right click on the RP which you want to “edit Claim rules” for.
c) Go to “Issuance Authorization Rules” and add a new rule. You will already see a “Permit all users” rule here. Leave it as is.
d) Click on “Add rules” and from the drop down select “send claims using a custom rule”.
e) Type the code below. The exists begins the condition where it detects where the request is coming from (external) and if the user is not the member of the group then =>issue command defines if both conditions are met then deny the claim.
exists([Type == “http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork”, Value == “false”])
&& NOT exists([Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid”, Value =~ “^(?i)group side to allow”])
=> issue(Type = “http://schemas.microsoft.com/authorization/claims/deny”, Value = “true”);
f) Add the claim rule. You should already have one more claim rule for “Permit All users”.
g) Make sure the new rule that we created in step e) is above the existing claim rule that permits all users.
This is how the claim issuance authorization rule works.
If step e) is evaluated and if the request is coming from internal network then the first condition is not met and it immediately moves to the next claim rule which is to allow/permit all users. This is how all users are able to access internally.
If step e) is evaluated and if the request is coming from external network then the first condition is met and it moves to check the next condition which is if the user is member of the group. If not member of the group then claim is denied. If user is member of the group then the claim rule moves to the next claim rule which is to permit the user.
Following the above steps we are able to achieve the requirement. As you can see the custom claim rules for ADFS are very powerful and if you understand the claim rules language it an be even more powerful allowing a lot of customization.