Receiving a list of values in a Claim from ADFS - asp.net-mvc

I am running an MVC5 project that authenticates with claims received from ADFS.
For a specific claim type, I need to include a list of organizations that the user has access to see.
Can this be done by including the list as properties of the Claim, or can it be done in any other way?
If so, how can I set up this Claim in ADFS?

The easiest way is to create a multi-valued attribute in AD for each user that contains the list of organizations.
Then have a normal AD mapping claim rule.
Note that this will not produce a list in one claim, rather multiple claims.
Refer: ADFS : Multi-valued attributes from AD.
The other way is to have each organization as a security group and then make the users membersOf each group as appropriate.
You can then create a group claim rule.
Refer: ADFS : Sending groups as claims.

Related

Checking app role assigned to a managed identity transitively

Input:
A list of managed identity
The name of app role that we are interested, for example User.Read.All on Microsoft Graph API resource.
Output:
All managed identity in this list that have this role assigned either directly or indirectly via AAD group. For the latter, the role is assigned to an AAD group which the managed identity is a member of.
For the direct assignment we can use the following rest endpoint described in the following doc:
https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list-approleassignments?view=graph-rest-1.0&tabs=http
For the latter, we need to be able to list the app role assignment transitively, we have tested the API above, it does not handle the transitive assignment. Interestingly, such transitive capability is available if app role is assigned to an AAD user.
https://learn.microsoft.com/en-us/graph/api/user-list-approleassignments?view=graph-rest-1.0&tabs=http
Our question:
Is there an endpoint that can provide such transitive checking?
We did come up with a work around. It is not perfect but at least it avoids the N rest endpoint call where N is the number of groups a managed identity belongs to.
Call Graph API to find all principals an app role is assigned to using app role list assigned-to endpoint.
https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list-approleassignedto?view=graph-rest-1.0&tabs=http
This will include both the service principal and the AAD group as the assignee. The result is a set called S1.
Call Graph API to find all groups a managed identity belongs to. The result is a set called S2.
Add the principal Id of the managed identity to S2
Calculate the intersection of S1 and S2. If the intersection is empty, the app role is not assigned and if the intersection is non-empty, the app role is assigned. The approach requires two rest calls.
Could you help validate this work around if there is no better solution? It seems to us this is a common use scenario, we wish there is a simple solution than the work around above in the future.

Securing URL with user owned resources in OAuth2

I'm aware of how OAuth2 and OIDC can use custom scopes and token introspection to secure an URL like this:
/users/me/documents
I can give this URL the documents:view scope and when receiving the token from the authenticated user, I can ask the authorization server if this user has the correct permissions. Then I can use the preferred_username claim or similar to see who /me actually is.
But what if I have a resource which is accessible by multiple users? Let's say a user has documents but they can be viewed by his direct manager. To retrieve the employee's documents as a manager, I'd need to have an url like this:
/users/${userId}/documents
How could I enforce it in a way that only the resource owner and direct manager can view this resource? I don't want everyone to access everyone's documents by knowing the userId. I could grant access as a whole to all users with the manager role, but that's not specific enough.
I'm aware there's the UMA extension where users can grant access to resources on his behalf to other users, but it's not the user who grants permission. It's the system who states in this case that managers can access their employees documents.
Would it make sense to write a custom policy which extracts the ${userId} and performs the check? Or should this not be done by the authorization server at all and be done by the resource server instead? Perhaps a different approach to reach the same goal?
Finer grained authorization like this is done with claims rather than scopes. There may be business rules around which docs a user can see, eg:
A user can access their own docs
An admin has view access to all docs
A manager can view docs for people they manage
In an access token this might be represented by these claims:
userId
role
Claims are often domain specific like this and the preferred option is to add them to tokens during token issuance. At Curity we have some good resources on this topic:
Claims Best Practices
CLAIMS AND AUTHORIZATION
The Authorization Server issues access tokens and then APIs (resource servers) verify the access token and use the token data to apply authorization rules (which are often domain specific) on every single request.
Claims are often used when dynamic behaviour is needed - they are runtime values that derive from the user identity, whereas scopes are fixed design time values. In your example an API might also need to vary SQL to retrieve documents based on the user identity.
There are more complex variations on this theme, such as an API calling a system such as Open Policy Agent, so that documents returned are determined by rules configured by a security administrator. That policy would still involve using claims from the access token though.
EXAMPLE CODE
If it helps, here is some sample code of mine that show the type of approach when enforcing domain specific authorization rules. Typically you need to filter collections and check access to individual items.

URL-based access management using Azure AD

I have simple ASP.NET MVC survey generator, that based on the URL displays various forms and persists data input from user:
http://survey.mydomain.com/ConferenceFeedback
http://survey.mydomain.com/DailyReport
etc.
Some of these subapps should be accessible by everyone, some by specific set of users. My user store is Azure AD. Is there is any Azure AD related feature that is a good match for my authorization scenario, where I could assign user rights on Azure side and just validate some claims or roles in my app per user request?
You might want to look into application roles. You can define roles representing the various subapps you want to model, and you can assign users to them accordingly. On the application side, you can examine the incoming claims set and decide whether the user has the necessary role claims for accessing the portion of the app they are requesting. See https://github.com/Azure-Samples/active-directory-dotnet-webapp-roleclaims for a sample app demonstrating the approach.

Using Directory Roles for authorization in ASP.NET MVC application using AAD

I am working on a ASP.NET MVC 5 app which depends on multi-tenant authentication using Azure Active Directory. I have authorization setup globally by adding AuthorizeAttribute filter. Now I want the app to be accessible only by users who belong to particular Directory Roles (Global Administrators and User Administrators to be specific). What is the best way to enforce this?
Roles in the principal only contains app specific roles. I was hoping that one of the identity claims would certify standard directory roles. But only claim that is relevant is the "groups" claim that tells me what Directory Roles AND Application Groups the user belongs to. I then need to query each group object ID to see if one of them is a Directory Role I want to allow.
Also, is there a way to do this globally by adding it as a filter?
Varun, in the coming weeks we are planning to add a new claim in the token that contains the well-known identifiers of the directory roles that the user belongs to. Stay tuned for that.
Until then the best option is to query the Graph API at login, determine the directory roles the user belongs to, and inject claims of type role in the claims principal. This will enable an authorize attribute filter.
Hope that helps.

Getting ADFS claims from AD when user logs in via Azure ACS

Our chain goes:
ASP.NET app with WIF -> ADFS -> and maybe Azure ACS -> Facebook, Google etc.
We have users configured in AD with roles etc. These users can log-on to AD via ADFS and get their roles as per normal.
Optionally, they can log-on to one of the ACS providers and we have a use case that stores the ACS provider's unique ID in AD. If they use more than one provider, we have more than one mapping.
So we can map the user who log ins in via ACS to their "real" identity in AD.
What we are battling with is how to deliver the full set of claims to the users who login via ACS? Typically, you just get a name, email address and unique id.
Is there a claim rule that can search AD using the unique ID? This rule would have to establish which provider they used in order to use the correct unique ID in AD.
I guess we could query AD from the application but that means we have to add the code to all such applications?
We could probably do the conversion in a custom STS as well?
Any ideas, good links, articles etc?
Your scenario might have less friction if your chain looked like this instead:
ASP.NET app with WIF -> Azure ACS -> (ADFS or Google or Facebook)
Is this a viable option?
ACS integrates best with ADFS when ADFS is acting as an identity provider rather than a relying party. Furthermore, in one hand ADFS will happily federate with external identity providers to grant access to users from foreign directories, but I don't think you can get ADFS to authenticate users from its own local AD directory using a token issued from ACS.
You can achieve this by creating a custom rule in ADFS using the Claim Rule Language in the Claims Provider Trust for ACS (see here and here for some language documentation).
But: I'm not sure if you can search the AD with the unique ID right away, as the param type used to query the AD isn't specified in the Claim Rule Language. The rule templates use the windows account name (layout: DOMAIN\USERNAME) to search, thus I'd recommend using a (custom) attribute store instead of AD itself and map the unique ID to a windows account name.
Assuming you have your attribute store set up, you can create custom rules that set the windows account name claim and enables you to query AD with the template rules of ADFS.
The custom rule would look something like this:
c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"]
=> add(store = "YourAttributeStore",
types = ("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"),
query = "{0}", param = c.Value);
To really enable the template rules you also need to set the issuer on the newly generated claim, because they check if it comes from "AD AUTHORITY". I don't know if this is a legit approach but I do this for convenience. This requires a second rule that will look something like this:
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"]
=> issue(Issuer = "AD AUTHORITY", OriginalIssuer = "AD AUTHORITY",
Type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname",
Value = c.Value);
Regarding the distinction of different unique ID providers, it's your choice on how to handle it. You could create a custom rule for every provider, let the attribute store make the distinction or make generic queries to your attribute store. The documentation of the Claim Rule Language should help you here.
Note: This seems to be a topic commonly avoided in books about ADFS/WIF/Claims based Identity. This is my personal solution and it may not be best practice, it's just the most convenient I came up with. If anyone knows coverage of this particular topic: please share.
Also note: rule order matters in ADFS, the claim(s) created in the first rule are available in the following rules and so on, this is what makes this possible.
Edit: didn't see this Question was asked one year ago ... hope this answer is helpful to someone anyway.

Resources