Adding custom claims into AAD token - azure-active-directory

Is it possible, while acquiring an access_token (Client Credentials grant), to instruct AAD to inject certain custom claims with certain values into the access_token being issued?
I need it to avoid sending extra context information to my service through such a "disconnected" means as HTTP Header for instance. Instead I want the token signed by AAD and containing everything AAD stamps into it by default plus some small pieces of information controlled by the application acquiring the token. All this will help my service to apply proper authorization once this token is received by the service.

I looked at the above, and I am clear that you are not looking for claims augmentation as it was described in the blog.
As I understood, you are looking for the right way to authorized your application using AAD tokens.
If my understanding is correct here is my answer.
It took me quite sometime to remember how I did it before and the caveat was missing the graph permissions for:
Directory.AccessAsUser.All
Directory.Read.All
Directory.ReadWrite.All
Now let me type down the steps one by one, but care less to the order of these steps may not be correct, just do the steps in any order you want.
Step 1: In AD, in the App registration
Register your Web Application,
Copy the Client_ID
Step 2: Go to Expose an API
Add a scope or more (This is what you are going to see as a claim and role in the token)
Add the client Client_ID
Note: this is basically for 2 applications one calling another, but in this example and your case, you have one web application that needs to authorize on itself.
Next: In the API permissions
THIS IS A MUST grant admin consent delegated permissions for MicrosoftGraph
Directory.AccessAsUser.All
Directory.Read.All
Directory.ReadWrite.All
Additionally: Give permission to the scope that you added.
Then: In the App roles:
Add the Application roles
Then: In the Enterprise Applications:
Assign that role to the users or groups that you want to access this.
Finally: In the application configuration file
Update the Client id
You are done.
I hope that was what you were looking for.

Related

How do I check if the user from a non-Graph Azure AD token is a member of an email distribution list in an APIM policy

Have an API in Azure API Management (APIM).
The API operation validates a JWT generated by Azure Active Directory (AAD) using a scope from a backend app registration (the scope is NOT User.Read). Note: client id is another app registration which is an authorized app of that backend scope.
After the JWT is validated, am I able take that token, extract user info out of it and verify if the user is part of a email distribution list (DL)? If so, how to do it within an APIM policy?
I am aware of MS Graph APIs. Using Postman I can confirm the DL is listed in the tenant's groups and can get its group ID. I can also confirm the user is a member of the group. The bit I'm stuck with for Graph API is that it needs a different token to the one supplied by the client application (due to he scopes being from different domains custom app registration vs graph) and I'm stuck at this point. Should I make the client app also get a graph token and pass it in a separate header, or is there way to orchestrate things from within APIM or something else?
The non-APIM part of this solution is provided by a Microsoft article. I've summarised those and combined with the APIM parts in the following steps:
In Azure, create a new Azure App Registration (note the client id for later)
Under "Certificates and secrets", add a client secret (note the secret for later)
Under "API Permissions", add a new MS Graph Application Permission (can be User.Read.All, Group.Read.All, GroupMember.Read.All depending on your situation). MS Graph's "groups" includes both AD groups and Distribution Lists (DL). Note: don't use Delegated permission.
Application permissions allow the authorized app to enquire about any user/group. You will need an Azure Admin to Grant Admin Consent for the App Registration to have the chosen Application Permission.
Now in Azure APIM, go to your API and edit the inbound policy.
Validate the JWT from the user making the call (See validate-jwt or newer validate-azure-ad-token) to ensure the User is authorized to call this API.
Extract the oid claim from the JWT (this is the user ID I'll use for the graph call) and save it in a variable using set-variable policy
Add a send-request policy request an auth token for MS Graph using client-credentials flow (this is when you'll need the client id and secret from earlier App registration). Note: secrets should be stored in a secure store like KeyVault but that is outside the scope of this answer.
Extract the access_token field from the JSON response body and put it in a variable using set-variable policy.
Create another send-request policy, but this time to the MS Graph endpoint. For User.Read.All permission you'd use /users/<userIdFromJwtOidClaim>/memberof/<groupId>. MS Graph v1.0 API Reference, and pass the access_token in the Authorization header using <set-header> element.
A status code of 200 indicates the user is a member of the group. IIRC A status code of 403 indicates the user isn't a member of the group.
Use a choose policy to perform logic depending on the user's group membership.
Use return-response policy to send a response back to the user.

What's the difference between User.Read and OpenID+Profile+Email scopes

Does User.Read "contain" the permissions email openid and profile? I've found that apps that are requesting the 3x scopes, can instead accept just the User.Read permission and still function equivalently
At work I'll get requests from the business to help them setup SSO using OIDC, and I'm not actually sure what permissions I should be giving them. Seems like either option works but I'd like to better understand what's happening
See my observations below:
I've created a basic Function App, and configured it to use OpenID Connect Image
My App Registration already has the User.Read permission with admin consent, so when I log into my Function, there's no issue.
Image
However, after removing the User.Read permission and logging in, I now get a permissions request prompt Image
And after consenting to the permissions, I can now see that email openid and profile permissions were added Image
Even more interesting, the permissions in the request prompt correlate to openid and offline_access, but offline_access wasn't added, while email and profile weren't in the request
I did find a similar question, but the accepted answer doesn't seem to align with what I see here
Generally I would favour the OAuth standard design where fields like these are Personally Identifiable Information (PII). So each app should only use the smallest scope it needs, as an information disclosure best practice. See also this Curity article.
Name
Email
Phone
Address
The Graph API can also be used with standard scopes, as in step 11 of this blog post of mine, where I wanted to get hold of user info in an API. So if this works for you I would prefer it. Personally I also prefer standard scopes so that my application code is portable.
Microsoft's design is based on each API requiring a different access token, the resource indicators spec. It is interesting, though perhaps not always intuitive. I am no expert on Azure AD though, and there may be some intended usage I do not understand.
User.Read is a scope intended to be used when requesting an access token for the Microsoft Graph API. It grants privileges to read the profile of the signed-in user only. A separate call to the Microsoft Graph API is required to retrieve the profile.
openid, email, profile and offline_access are OpenID Connect scopes:
openid is used to request an id token.
offline_access is used to request a refresh token which can later be used to get a new access token.
email to request an email claim.
profile to request several user claims (Eg.preferred_username).
Both email and profile can be used to augment information available in the UserInfo endpoint, however, it is recommended to use the id token which is already a superset of the information available at the aforementioned endpoint.

Azure AD: How to get both security groups and application roles into access token

I am facing a scenario where my .NET Core app (Azure Web API) will be accessed in two ways:
client_credentials flow
delegated permissions (user) flow
I am using Microsoft Identity Web to authorize and authenticate requests via AD on middleware level. Then I would like to do additional authorization inside my controller methods to check the following:
In case of application call (client credentials), check that the provided access_token contains a specific roles claim that matches with the application role defined in app registrations -> app roles.
In case of signed-in user call, check that the provided access_token contains specific AD Groups (security groups) assigned to that user in Azure AD.
Flow #1 works, but if I enable flow #2 by clicking on token configuration -> add groups claim -> Security Groups -> emit groups as role claims in access token, then the app roles are no longer available in the client credentials flow inside the access token (as in the below screenshot), presumably because it overwrites the roles claim with the security groups (which do not exist for applications).
What is the correct way to do this, or achieve an equivalent situation in a different way?
The requirement is to differentiate controller method access where application A can call e.g. a read-only endpoint 1, but cannot call write endpoint 2, whereas application B is able to call write endpoint 2. The same differentiation should be done also for users on AD-group basis.
You can include a groups claim in your token. You just need to modify the "groupMembershipClaims" field in application manifest:
"groupMembershipClaims": "SecurityGroup"
Then the token will contain the Ids of the groups that the use belongs to like below :
{
"groups": ["{group_id}"]
}
This method won't overwrite the roles claim. So for delegated permissions (user) flow, you need to check the groups claim instead now.

Separate UserStore By Client

I'm using Identityserver4 and ASPIdentity for user management. I want users dont have access to all applications(clients). I'm going to add table like UserClients, which specifies allowed applications for user. I'm interested in your advice, whether it is possible and does not conflict with the principles of SSO?
IdentityServer takes care of client access configuration and user authentication, but not of authorization at client/user level.
Users are not bound to a client. Thus, as you've noticed, all users have access to a client. IdentityServer authenticates the user, regardless which client is used or whether the user logs in at IdentityServer directly.
So it is the client (and also api) that has to authorize the user.
It doesn't matter if the user is already authenticated (SSO) or not. It is the responsibility of the client / api to determine whether the user is authorized. In order to solve this 'problem' they've build the PolicyServer, to decouple authentication and authorization.
By adding middleware (as done in PolicyServer) you can grant or deny a user access.
You can add some discriminator in IdentityServer, e.g. a specific claim type / role, use a seperate authorization server or use resource based authorization, e.g. when a (business) user is present then the user has access.
In the end, authorization is done by the resource that needs to be protected, because only the resource can interpret the claims and business rules.
All you have to do is short-circuit the pipeline in case the authenticated user has no access.
It's perfectly fine if you want to 'disable' SSO. Simply ignore the IdentityServer cookie (in the client), thus forcing the user to login (for that client).
But you should not deny authentication (e.g. return invalid login) when the user has no access to the client / resource.
That's why there is http status 401 Unauthorized and 403 Forbidden. So you can notify the user that it is not authorized to use this client.
Regarding your setup, in your case you can also add claims to the UserClaims table (instead of creating an additional table) to configure the allowed apps, e.g. user A:
http://mycompany/appname = app1
http://mycompany/appname = app2
http://mycompany/appname = app3
Add the ClaimType to the IdentityClaims table and the ApiClaims table in order to make sure the claims make it to the access / identity token.
Add middleware in the client / resource or add business rules (policies) where you check the claimtype and required value. If not present then deny access. E.g. app1 should look for the required claim type http://mycompany/appname with the value 'app1'.

Microsoft Graph API - new delegated permission removing application permissions

I'm using the v1 Azure AD auth URLs (/common/oauth2/authorize) for a multi-tenant app that requires admin_consent.
I've attempted to add a new scope Directory.AccessAsUser.All. It is the first 'delegated' permission I'm requesting when all my other scopes are 'application' level permissions.
When I added that new delegated scope and prompted the admin to re-consent, the other scopes disappeared from the returned AccessToken and the responses scope parameter. Only Directory.AccessAsUser.All is present in the access_token scp field.
Is there any reason this behavior would occur? I'm positive that we are promoting for admin_consent and that an admin is the one consenting.
The scopes specified in the scp will depend on which OAUTH flow you used to obtain the token. You cannot have a single access_token with both Delegated and Application scopes.
Application scopes are applied when using the Client Credentials flow (client_credentials).
Delegated scopes are applied when using either Authorization Code or Implicit flows (authorization_code or implicit).
Update: I've written a more in-depth post about this topic that might help folks facing similar issues: Application vs Delegated Scopes.

Resources