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 - azure-active-directory

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.

Related

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.

Azure AD Enterprise Application - Identify Caller Application

I have a REST API which uses Azure ADD App registration to allow other apps to call it.
In the Azure Portal, I have registered it as an Enterprise Application and also registered the consumer applications and assigned them Roles appropriately.
The authentication and RBAC works fine.
But the use case that I am working on requires me to identify and log the incoming request calling application's name (The one seen in the portal as 'Display Name', when we view the list of users and groups for an enterprise Application).
As advised in the internet, I am using some Identity related API to read the claims from the request header.
var provider = claimsUser.FindFirst("http://schemas.microsoft.com/identity/claims/identityprovider").Value;
var sid = claimsUser.FindFirst(ClaimTypes.NameIdentifier).Value;
OR
var sid = claimsUser.FindFirst("stable_sid").Value;
But this gives me a GUID value which I couldn't map to any of the consumers of the Enterprise Application.
The clients are all registered in the Azure portal.
In the Portal, I can see the apps in the "Users and Groups" section for the Enterprise application, with their appropriate roles.
In terms of usage, before making the call, the clients generate a bearer token based on the certificate that they get from Azure. The make the call with the bearer token attached to the request header. This bearer token is validated against the Azure AD, in the filters set before every controller..
What I want is to get are the details about this client who has made the call.. As per some repliers, and, to which I agree, the Guid that I get as part of the previous call mentioned above is for the actual user and not the app itself which is making the call.
Can anyone throw some light into it.. some code snippet will be of real help..
I'm not sure what authentication flow you are using, but SID is generally for a user that's logged in, not an application. if your client applications are using client id and secret, the token it returns that you send to the api should include the app registration guid. https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#access-control-lists . So the appid and iss should give you the guid of the app registration. using this, you can make a call to graph api, to identify the display name of the app registration. https://learn.microsoft.com/en-us/graph/api/application-get?view=graph-rest-1.0&tabs=http
If your app is a user login app, and you don't want to make a call to graph, the other option you could do as a workaround would be to create app roles and assign users to them but name the app roles with some convention that includes the app's display name. then the name could come through under roles claim.. https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
those are some options.. but other than calling graph or kinda working around to inject the name into a different claim of the token I'm not sure of any other method to get the "app registration's display name"

How to access azure digital twin API using Service Principal?

My use case is whenever i get a trigger from Cosmos DB in Azure functions, need to interact with Azure digital twin APIs without any human interaction.
From the below link, I understood we can use service principal to achieve it.
Is it possible to configure Azure Digital Twins API access for a Daemon App?
But I don't know how to authenticate service principal with digital twin APIs.
1)What type of authentication is required and how the flow will be?
2)If it is Oauth2, what is the grant type and scope for accessing digital twin?
Thanks in advance.
There is an (almost) undocumented way to use the Digital Twins API without an On-Behalf-Of flow. I use it for automated tasks to manipulate the contents of ADT or to give certain applications read-only view of the data. It all starts with a role assignment. See this snippet from the YAML that I use to provision my ADT instance when I first make it.
- roleId: 98e44ad7-28d4-4007-853b-b9968ad132d1 # Space Administrator
objectId: abcd1234-5556-44a2-1234-402dbd999619 # Service Principal object ID
objectIdType: ServicePrincipalId
tenantId: 1234567-8901-2345-abcd-123456789 # Azure subscription tenant
The ServicePrincipalId object type is described on this page but is never mentioned in any of the samples again. This snippet gives Space Administrator rights to a service principal. You can then use a client secret to retrieve an access token that will allow you access to ADT. When making an app registration for ADT in your Azure Active Directory, go to Certificates & Secrets and make a new client secret.
The next step is to retrieve the objectId of the Service Principal, this is not the objectId of the application registration. When you go to the Overview tab of your App Registration you can copy the Application ID and perform the following command in the cloud console:
az ad sp show --id {the id you copied}
This will show a lot of details about your Service Principal including the objected. Copy this as well.
Almost there, to retrieve an Access Token you need 4 things:
Authority: https://login.microsoftonline.com/{your tenant id}
ClientId: The application id of your app registration.
ClientSecret: The client secret you created.
DigitalTwinsAppId: This is always 0b07f429-9f4b-4714-9392-cc5e8e80c8b0
Retrieving the Access Token in .NET Core
var authContext = new AuthenticationContext({Authority});
var clientCredential = new ClientCredential({ClientId}, {ClientSecret});
var result = await authContext.AcquireTokenAsync({DigitalTwinsAppId}, clientCredential);
return result.AccessToken;
Add that to your headers (HttpClient example below) and you are good to go!
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
1)What type of authentication is required and how the flow will be?
As the post you have referred to, you should use OAuth 2.0 On-Behalf-Of flow.
The main flow is here: Call Digital Twins from a middle-tier web API.
2)If it is Oauth2, what is the grant type and scope for accessing
digital twin?
You can refer to this sample:
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
And for scope, it should be the digital twin API you want to access. (eg. spaces, devices, users or sensors). See API summary.

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'.

Azure client credentials grant oath not working in hybrid setup for Graph Mail API access

In hybrid setup if client credentials grant type is used to get token and if that token is used to get on-prem user messages (https://graph.microsoft.com/v1.0/users('onpremuser#onpremdomain.com')/messages/) using graph api it fails by providing UnknownError.
When debugged on IIS logs error shown was "This token profile 'V1S2SAppOnly' is not applicable for the current protocol." error_category="invalid_token".
However if authorization code grant or resource owner password credential (ROPC) grant if used to obtain token , we were able to get messages of on prem user using graph API.
Have attached screenshot of token for both. How to make client credentials grant work for on-prem user messages access using graph API (in hybrid setup) ?
Update
Update i went and edited web.config of rest in Exchange server to have V1S2SAppOnly in profiles. After that previous error is gone and new error is seen.
Bearer+client_id="00000002-0000-0ff1-ce00-000000000000",+trusted_issuers="00000001-0000-0000-c000-000000000000#ea6064aa-d6fc-48d3-abb8-1728e1f39e0b",+token_types="app_asserted_user_v1+service_asserted_app_v1",+error="invalid_token" 2000008;reason="The+token+should+have+valid+permissions+or+linked+account+associated+with+partner+application+'00000003-0000-0000-c000-000000000000'.";error_category="invalid_grant"
I think the problem is with the aud claim, i.e. the audience for token.
For the first token that you have shared
aud value is 00000002-0000-0000-c000-000000000000. This is the resource Id for Azure AD Graph API and not Microsoft Graph API. For Microsoft Graph API, you should be using https://graph.microsoft.com or Id 00000003-0000-0000-c000-000000000000
this token is probably the one where you used client credentials grant, as there isn't any user claim
For the second token that you have shared
aud value is https://graph.microsoft.com which is correct
this token is acquired in context of a user name anoop so I guess this is the one which is working for you.
What you want is:
Application with Client credentials => Graph API => Local Exchange.
This scenario isn't supported out-of-the-box, but you can however tell your local exchange server to accept those tokens. See this answer https://stackoverflow.com/a/56131954/639153
In a nutshell, you need to change the authentication config of your front-end exchange servers to accept client credentials from the graph api. By default only delegated credentials are supported, and these settings are not documented on the exchange side.
Warning, we tested these settings, and it's working but not supported by Microsoft
This is the blog where I've found the answer to your question. https://blog.thenetw.org/2019/05/13/using-client_credentials-with-microsoft-graph-in-hybrid-exchange-setup/

Resources