Microsoft OAuth authorization for specific user roles - azure-active-directory

I have to limit which users can access an Azure App. For now, only Global Admins can access using this link:
login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=client_id_goes_here&scope=user.read.all&response_type=code&redirect_uri=https://myredirectbacklink.com/aad/auth&response_mode=query&state=portal&prompt=consent
After redirect back I get Token using
https://login.microsoftonline.com/common/oauth2/v2.0/token and the query contains the same scope as the authorized link.
The flow of the app is the same as documented in Microsoft identity platform and OAuth 2.0 authorization code flow.
My problem is that client wants to allow Billing Admins to access their app. I went through all Graph Permission Scopes, but could not find any related to Billing Admin.
My knowledge of Microsoft's authorization is somewhat limited. I do know that limitations are done by scope. But if it's just link change or is it in Authentication App in Azure (there is one but I don't know how it's related to actual login permissions).
Should I look in a different direction or is it just simply changing the link? Microsoft's documentation didn't help a lot because it's mostly about calendars and other simple stuff.

Careful, requesting a permission that normally requires admin consent and causing the user consent prompt is not the proper way to ensure the user signing in is actually an admin. A non-admin user could simply modify the URL to scope=User.ReadBasic.All and remove prompt=consent. If the user is allowed to consent for User.ReadBasic.All (which is true in many organizations), they'd be able to continue the sign-in. (Or if the organization had granted admin consent for "User.Read.All", the non-admin user would only need to remove prompt=consent.)
If you need to ensure the user is an administrator, you need to explicitly check for directory role assignments.
You can choose from one of three different ways to do this:
You can configure your app to receive the wids claim, which will include the role template IDs of the directory roles for which the user has an active assignment. This is probably the simplest approach.
Using the Azure portal, you can do this under App registrations > (choose your app) > Token configuration > + Add groups claim. You must include "Directory roles" in your selection:
Another option is to a Microsoft Graph API request to check which of a given list of directory roles the user has been assigned:
POST https://graph.microsoft.com/v1.0/me/checkMemberObjects
Content-type: application/json
{
"ids": [
"fdd7a751-b60b-444a-984c-02652fe8fa1c",
"b0f54661-2d74-4c50-afa3-1ec803f12efe"
]
}
A third option is to make a Microsoft Graph API request to list the directory role assignments granted to the user:
GET https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments
?$filter=principalId eq '{id}'
All three of these approaches involve using directory role template IDs to identify the directory role you are checking for. They're all listed here: https://learn.microsoft.com/azure/active-directory/roles/permissions-reference
Some examples you may be interested in:
Application administrator: 9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3
Cloud application administrator: 158c047a-c907-4556-b7ef-446551a6b5f7
Global administrator: fdd7a751-b60b-444a-984c-02652fe8fa1c
Privileged role administrator: e8611ab8-c189-46e8-94e1-60213ab1f814
Billing administrator: b0f54661-2d74-4c50-afa3-1ec803f12efe
(I included the first four because those are the directory roles which would, by default, be allowed to grant consent for User.Read.All.)

If someone needs solution and uses php:
You can use https://github.com/microsoftgraph/msgraph-sdk-php
$accessToken = 'token from redirect back, called access_token';
$body = [
"ids" => [
"fdd7a751-b60b-444a-984c-02652fe8fa1c",
"b0f54661-2d74-4c50-afa3-1ec803f12efe"
]
];
$graph = new Graph();
$graph->setAccessToken($accessToken);
$user = $graph->createRequest("post", "/me/checkMemberObjects")
->attachBody($body)
->execute();

Related

HashiCorp Vault - Deny login access to Root namespace for non-Admin users

As part of our Azure AD app registration for Vault single sign-on using OIDC, we've created two Azure AD groups:
VaultAdmins : This group will have Admin access across all namespaces, including the Root
VaultUsers : This group will only have standard access on all designated child namespace(s).
We also have some dedicated Vault policies setup to define capabilities for the above two external groups. I must also mention that both groups are mapped to two internal (Vault) groups.
The requirement now is to completely deny OIDC login access to the Root namespace for all members of the non-Admin group. The non-Admin group ("VaultUsers") should only have the capability to log into the child namespace(s) to which they've been designated.
I have tried various things, inclduing setting up the below single rule for non-Admin users on the Root namespace, but I'm still unable to achieve the desired result. Any suggestions? Do I need to add an extra rule to deny access to the Root namespace and if so, how?
[[ Below, a snippet of the Policy rule for the Root namespace non-Admin users ]]
rule {
path = "sys/auth/oidc/*"
capabilities = ["deny"]
description = "deny all non-Admin OIDC access on Root namespace"
}
The sys/auth/oidc path is not used for authentication. It is used to enable and confige authentication backends, not to authenticate with them.
Second, your policy HCL syntax is wrong, for that configuration (not login) path. Your snippet should be written like this:
#Deny all non-Admin OIDC **configuration** access on root namespace
path "sys/auth/oidc/*" {
capabilities = ["deny"]
}
Finally, authentication endpoints can't be secured with policies because they are used by anonymous users. In other words, Vault doesn't know what group you are in until you authenticate, and then it's too late.
Keeping in mind that the OIDC login method actually delegates the authentication decision to Azure AD. Vault does not authenticate the user but it validates and trusts the authentication token returned by AzureAD.
So to acheive what you want, implement some kind of logic in AzureAD so that a user can't authenticate there (AzureAD will never authenticate non-admin users) for the Vault application only.
On the Vault side, the easiest way is to let users authenticate using Azure AD but not assign them any policies so they see nothing (but their cubbyhole). Using the identity backend, putting entity aliases in Vault groups can help, but it has some drawbacks you should evaluate against the pure AzureAD solution.

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.

user does not exists in the tenant directory error when calling microsoft graph api

I'am trying to call microsoft graph api, I have did the instructions by microsoft documnets as bellow:
1- app registration in azure portal
Supported account types : all microsoft account users
2- calling 'https://login.microsoftonline.com/',tenant_id,'/oauth2/v2.0/authorize' by these parameters:
client_id <- #Application Id - on the azure app overview page
client_secret <-# the secret key for my app from azure portal
scope <- 'https://graph.microsoft.com/.default'
grant_type <- 'password'
username <- 'XXX#outlook.com'
password <- # the user password
tenant_id <-# tenant id for my app from azure portal
but it has this error:
AADSTS50034: The user account {EmailHidden} does not exist in the <tenant_ID> directory. To sign into this application, the account must be added to the directory.
I have registered my app by the same account that I passed through api.
I want to call my todo list from the graph.
based on #MdFaridUddinKiron's response I added some more explanation:
I think something in my domain in azure is wrong, these are some screenshot of it:
1- it shows "common" for endpoints, what should I use? "common" or my tenant:
2- my app authentication tab has some differences, is it important?
3- My domain overview page is look like this:
4- user is added in my active directory 5- user application page:
6- user assigned role:
I tested microsoft graph api successfully by the same user in the graph explorer, I am getting confused how the authentication flow must be.
I just want to call my own todo tasks
As per your comment, please follow the detail steps:
Make sure email you are trying to get token with is exists in azure ad
user list which showed below in details.
https://login.microsoftonline.com/YourTenant.onmicrosoft.com/oauth2/v2.0/token
client_id:b603c7be_Client_id_e61f925
scope:https://graph.microsoft.com/.default
client_secret:NpmwO/KDJ_client_secret:NpmwO_W0kWf1SbnL
username:tenentUser.onmicrosoft.com
password:YourUserPassword
grant_type:password
See the screen shot:
I am getting token as expected
Step: 1
Step: 2
Step: 3
Step: 4
Filter your user from your azure active directory user list as shown below.
Note:
Requested token user must be a tenant user for example YourUser#Yourtenant.onmicrosoft.com
User password must be correct that you are suing to token request.
Make sure your user belong to azure portal on your tenant
Your Client Id belongs to that tenant
Application secret is valid or not expired.
Update:
What should I use? "common" or my tenant?
It depends if you have many tenant in that case you can use common.
For example user need not to remember specific tenant they would
automatically redirected to specific tenant as per the credentials
they given.
For more information you could refer Official document
Feel free to share still you are having problem.

Adding custom claims into AAD token

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.

Resources