AAD Graph User Patch authorization issue - azure-active-directory

I am getting an unauthorized error when trying to PATCH a User entity with AAD Graph (in a B2C tenant). The token, acquired using Client Creds flow with symmetric key has Directory.ReadWrite.All permission for graph.windows.net resource. What am I missing? (Must the token be acquired using X509 perhaps?)
Response:
{"odata.error":{"code":"Authorization_RequestDenied","message":{"lang":"en","value":"Insufficient privileges to complete the operation."}}}
Request
https://graph.windows.net/xyz.onmicrosoft.com/users/c064a6a5-...1f5a0?api-version=1.6
Content-Type: application/json
Content-Length: 241
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IndVTG1ZZnNxZFF1V3RWXy1oeFZ0REpKWk00USIsImtpZCI6IndVTG1ZZnNxZFF1V3RWXy1oeFZ0REpKWk00USJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLndpbmRvd3MubmV0LyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzQwZmFiMTlkLTQ0ZjctNDY4MS04YTBhLWIyY2FlMzQ1ZGUwMy8iLCJpYXQiOjE1NDEwODU1MzUsIm5iZiI6MTU0MTA4NTUzNSwiZXhwIjoxNTQxMDg5NDM1LCJhaW8iOiI0MlJnWUZqL09ubUNscnpiTEZ1VFRVcytaQ1ZOQUFBPSIsImFwcGlkIjoiNDU1YzVjMzgtNDhlOC00MWZiLWEzMmMtZmQyZTc2YTc0YzllIiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNDBmYWIxOWQtNDRmNy00NjgxLThhMGEtYjJjYWUzNDVkZTAzLyIsIm9pZCI6IjFmZGQzNDllLTUxZjMtNDkwNy05ZTg0LTZhMWQ5NjVmYWU3NyIsInJvbGVzIjpbIkRpcmVjdG9yeS5SZWFkV3JpdGUuQWxsIl0sInN1YiI6IjFmZGQzNDllLTUxZjMtNDkwNy05ZTg0LTZhMWQ5NjVmYWU3NyIsInRlbmFudF9yZWdpb25fc2NvcGUiOiJOQSIsInRpZCI6IjQwZmFiMTlkLTQ0ZjctNDY4MS04YTBhLWIyY2FlMzQ1ZGUwMyIsInV0aSI6Ii1XV0EwVjA3elVhbUdrZjA0TUFiQUEiLCJ2ZXIiOiIxLjAifQ.ZAARkTCSK1MYDj3TnDY8djUxienvmNySn0sNR1iZ4eDdYLSrtqjVG2E9EL4nlSYIIP92HZ6io3MdASxsfHrbZaSTvgy6gqe2dNJZ_aWh23TyHop3q5ctLCTqQpNEP1AGcq6vnXk2ceN5CMXkzK1d8R3Zlwa3ICo7lWFDKDEea0_Y87Hvm8U2-zjgzhqAiZi6sH3u7BxiZBqWop4Jn9Wddv2qq_lGU7UuzEwbTMFQ87BKWvts3K_H4UnzZDvDrwSi_GrwvG9VBQ1ST66qhGLRESnW0u_

I am getting an unauthorized error when trying to PATCH a User entity with AAD Graph
I suppose you are using the aad graph to reset a user's password.
As the doc mentioned, for Directory.ReadWrite.All, No rights to reset user passwords.
If you want to reset the password, it also has been mentioned in the doc, you should use delegated scope User.ReadWrite.All or Directory.AccessAsUser.All permission.
Important
Either delegated scope User.ReadWrite.All or Directory.AccessAsUser.All is required to reset a user's password. In addition to the correct scope, the signed-in user would need sufficient privileges to reset another user's password.

Related

Python MSAL - admin consent required error, when consent is already given

I am trying to request the Directory.Read.All scope using Microsoft's MSAL library & retrieve a valid access/refresh token pair with the acquire_token_by_authorization_code function.
I ask an organization's admin to consent to the above application permission with the /adminconsent endpoint (and have confirmed that permissions have been granted - see screenshot below). However, when trying to retrieve an access token with the Directory.Read.All scope, I get the following error:
{'error': 'invalid_grant', 'error_description': "AADSTS65001: The user or administrator has not consented to use the application with ID 'XXX' named 'XXX'. Send an interactive authorization request for this user and resource [...]'suberror': 'consent_required'}
Not exactly sure how this can be the case - as the permission is clearly granted. I have tried resetting the token cache by removing all MSAL accounts, but doubt this is the root of the issue, as the actual step that fails is converting an authorization code to access token.
Thanks for any help!
I can reproduce your error:
If you use the auth code flow to obtain an access token, you should grant delegated permissions, because this is a authorization flow with user interaction, so try to grant Directory.Read.All delegated permissions, and then grant admin consent for the permissions.

What Scope and or resource to set to get token that works with office 365 api?

We have acquired the Admin consent in both delegated and application ServiceHealth.Read permissions in Office 365 Management API for our Client app in Azure AD.
We are unable to figure out what the scope and or resource needs to be in token acquisition process if we want to make calls to the office365 management api.
Whether its the client_credentials grant method of direct token acquisition
Or the authorization code then token for signed-in user method
It would be preferable if its for the client_credentials grant method, but if it has to be through auth code, that's fine too.
We can use the following already to grab our reports but do not know how to allow that authentication to also cover Office365 Management API Service Health
curl --location --request GET "https://login.microsoftonline.com/{tenantid}/oauth2/v2.0/token" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data "client_id={clientid}&client_secret={clientsecret}&scope=https://graph.microsoft.com/.default&grant_type=client_credentials"
When adding ServiceHealth.Read to the end or by itself it returned invalid_scope as an error
When place only https://manage.office.com/ServiceHealth.Read/.default in the scope it gives the error invalid_resource with the description including that the resource not found in tenant
A similar problem occurred when trying to get the authorization code and setting the resource as ServiceHealth.Read and while setting that as the scope instead gave a authorization code, the resulting token was deemed invalid.
Authorization Code Grant Flow
I quickly tried this out with an Azure AD app registration that has ServiceHealth.Read delegated permission for Office 365 Management APIs.
Scope value used - https://manage.office.com/ServiceHealth.Read
I was able to successfully get back an access token following the Authorization Code Grant flow. I'll share the detailed request parameters passed shortly, but this should answer your direct question about what scope value to use.
Since I used Azure AD V2 endpoints, I didn't really need to specify a resource. In your sample requests mentioned in question I see that you are also using Azure AD V2 endpoint.
Detailed steps
Step 1 - Get the Authorization Code
For this step, I directly used browser and then sign in using a valid user from my Azure AD tenant.
// Line breaks only for clear reading. Remove line breaks and paste in browser URL to test.
https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/v2.0/authorize?
client_id=29a95b.....
&response_type=code
&redirect_uri=https://rohitapp/
&response_mode=query
&scope=https://manage.office.com/ServiceHealth.Read
&state=12345
Response should be something like
https://rohitapp/?code=
OAQABAAIAAACQN9QBRU....
&state=12345&session_state=f5da06....
Step 2 - Acquire Token from token endpoint
Take the Authorization code from last step.
For this step I used POSTMAN. You can use CURL as well.
POST https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
Request Body
client_id=29a95b....
&scope=https://manage.office.com/ServiceHealth.Read
&code=OAQABAAIAAACQN9QBRU....
&redirect_uri=https://rohitapp/
&grant_type=authorization_code
&client_secret=Aj....
Final Token received, decoded in https://jwt.ms
Client Credentials Grant Flow
Scope value used - https://manage.office.com/.default
I did add the related app permission and give consent for it.
For this one I used POSTMAN again. You can use CURL as well.
POST https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
Request Body
client_id=29a95....
&scope=https://manage.office.com/.default
&grant_type=client_credentials
&client_secret=Aj....
Final Token received, decoded in https://jwt.ms
Take a look at this Microsoft documentation around scope value for Client Credentials Grant.

Microsoft Graph Admin Consent Flow Doesn't Contain Refresh Token

Is it possible to get refresh token also in the admin consent flow?
I've received access_token but not refresh_token even though I've included the offline_access permission in the delegated permissions.
to be more clear: I need to get to the users drive files (i.e get/update files) of the entire tenant (organization). therefore i'm requesting Application Permissions and Using the admin consent endpoint.
therefore I use the client_credentials grant in order to get the entire tenant access_token but unfortunately with no refresh_token in order to extend time of privileges of the tenant for more then 1 hour.
if i misuse the protocols above clarification will be highly appreciated
While Marc's answer is correct, I think the whole thing can be explained in a little more detail:
To get full access to a tenant (i.e. not just resources associated with the current consenting user), then you need to use the Admin Consent flow. However, unlike other flows in Graph's OAuth, you do not require a refresh_token to maintain access. Instead, you can simply request an access_token at any time once an admin has consented. Each access_token lasts 60 minutes.
Having first used other flows such as the Management API and Graph's Code Grant flow, this one confused me, but it's actually even simpler than those.
To get consent, simply direct the admin to the following URL:
https://login.microsoftonline.com/[Their Tenant ID]/adminconsent?client_id=[Your Client ID]&redirect_uri=[Your Redirect URI]
Your redirect URI will get a response with params:
?admin_consent=True&tenant=[Their Tenant ID]
Once you've received this confirmation, you can request an access_token at any time by sending a POST request with the following form fields:
client_id: [Your Client ID],
client_secret: [Your Client Secret],
scope: 'https://graph.microsoft.com/.default',
grant_type: 'client_credentials'
To the following URI:
https://login.microsoftonline.com/[Their Tenant ID]/oauth2/v2.0/token
The response will contain a new 60 minute access_token and you can simply call it again whenever required.
Refresh Tokens are only returned when both offline_access is requested and you are using the Authorization Code Grant.
More importantly, if you are receiving an access_token then you are not executing the Admin Consent workflow. Administrative Consent is only used for consenting to your application's scopes. The Admin Consent response does not contain an access_token, it only contains the Tenant ID (for the tenant that was consented) and a boolean that tells you if consent was granted as query parameters:
http://{return_uri}/?tenant=[tenant id]&admin_consent=[True/False]
If you are receiving an access_token then you are using either the Authorization Code, Client Credentials, or Implicit grants.
UPDATE:
There is no refresh token issued for Client Credentials, you simply request a new token from the /token endpoint as needed.

Microsoft Graph API Password Reset Only Accepts Null password

I am trying to reset another user's password through the Graph API (specifically graph.windows.net, though the same behavior occurs using graph.microsoft.com as well).
I believe all security is properly set up. The application registration has the Directory.ReadWrite.All permissions and the service principal is a Global Administrator. There are no errors regarding insufficient privileges, but whenever I try to provide a password inside the password profile I get an error.
PATCH request to
https://graph.windows.net/{tenant_id}/users/{userPrincipalName}?api-version=1.6
with the following body gives me a 204 response.
{
"passwordProfile": {
"password": null,
"forceChangePasswordNextSignIn": true
}
}
But the same request with a password of "P#ssword1" or any other valid password returns a 400 Bad Request with the message "One or more properties contains invalid values."
Anyone encountered anything like this? These are accounts being synced from local AD but the password reset has been turned on in Azure and you can manually reset the passwords in AAD. Any help or advice is much appreciated.
It turns out this was a problem with how AD sync is set up in their environment. Apparently a federated domain syncing to Azure AD locks out the ability to reset passwords via the Graph API but the error messages don't tell you its a permissions issue.
It's outline here under unsupported operations, my problem was not understanding the full AD environment set-up: https://learn.microsoft.com/en-us/azure/active-directory/authentication/concept-sspr-writeback
According to your description, I assume you want update user's password through the Graph API.
According to this document, when we update the passwordProfile property, the following permission is required: Directory.AccessAsUser.All.
Based on my test, we can modify someone's password by using the following steps:
Grant the permission by following this document.
Check the password in the profile whether satisfy minimum requirements as specified by the passwordPolicies property.
Use the following request to update someone's password.
The Request URL:
PATCH /users/{id | userPrincipalName}
And the request body:
{
"passwordProfile": {
"forceChangePasswordNextSignIn": true,
"password": "P#assword1"
}
}
If successful, this request returns a 204 No Content response code.
You're calling the wrong URL, graph.windows.net is the legacy Azure AD Graph API. The Microsoft Graph APIs are located at graph.microsoft.com. While these APIs are similar in function, they're calling paterns are very different.
As kikang mentioned, in order to change a user's password you need need to request the Directory.AccessAsUser.All scope. There are a few important cavetes with this scope:
This is a Delegated scope, so it can only be requested when using Authorization Code or Implicit OAuth flows. It cannot be used with Client Credentials.
Before a User can consent to Director.AccessAsUser.All, you must first obtain Admin Consent from an Admin on the user's AAD tenant/instance.
Once you have the proper scopes consented, you need to issue a PATCH to the /user resource.
Your call will look similar to this:
PATCH https://graph.microsoft.com/v1.0/me
Content-type: application/json
{
"passwordProfile": {
"forceChangePasswordNextSignIn": true,
"password": "A-Strong-Password"
}
}

Can service/daemon app use 'common' instead of 'tenant-id' when obtaining token from Azure AD v2.0 endpoint

I am developing a daemon app that can perform actions on user inboxes in multiple tenants. Once an admin from a tenant gives the app the required permissions, it should be able to access users inboxes in that tenant. I am doing the following now.
Get consent from admin
https://login.microsoftonline.com/common/adminconsent?client_id={client-id}&redirect_uri=https%3A%2F%2Fredirect.test.com
Global Admin from a tenant (e.g. myutils.onmcirosoft.com) gives required permission.
Get access token by using common instead of tenant-id
curl -X POST _https://login.microsoftonline.com/common/oauth2/v2.0/token --data "grant_type=client_credentials&client_id={client-id}&client_secret={clientsecret}&scope=https://graph.microsoft.com/.default
It gives a access token.
Access messages of admin#myutils.onmicrosoft.com using the token obtained in step 3.
curl -X GET -H "Authorization: Bearer <token>" https://graph.microsoft.com/v1.0/users/admin#myutils.onmicrosoft.com/messages
Above curl request gives the following 404 response:
HTTP/1.1 404 Not Found
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: text/plain
request-id: e602ada7-6efd-4e18-a979-63c02b9f3c76
client-request-id: e602ada7-6efd-4e18-a979-63c02b9f3c76
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"West US","Slice":"SliceB","ScaleUnit":"000","Host":"AGSFE_IN_22","ADSiteName":"WST"}}
Duration: 1537.3097
Date: Tue, 19 Sep 2017 09:31:08 GMT
In step 3 if I replace common with tenant-id of myutils.onmicrosoft.com it is working.
Is common supported with Azure AD v2.0 endpoint? This link says common is not supported for v1 endpoint. Is it the same for v2.0 endpoint as well?
Just to expand on Nan Yo's answer, the reason you need the tenant-id is due to Graph API itself being a multi-tenant.
Without a tenant-id, Graph would have no way of knowing which tenant to route your calls too. For example,/users returns a list of users but without a tenant-id there is no way to determine which tenant it should pull the users from.
When you use authorization_code or implicit grants, it uses the user's credentials to determine which tenant calls should be routed too. With client_credentials you don't have those credentials to pull those routing queues from.
You can however determine the tenant id during Admin Consent. Along with the admin_consent result, it also returns the tenant. For a multi-tenant service you would need to store that id to use when obtaining a token prior to making a call to Graph.
When using the client credentials flow in Azure AD V2.0, you need to specify the actual tenant that you want a token for. Otherwise , even you get an access token , you will find no application roles include in the access token . An access token will issued from one tenant and could access resource of that tenant , it is impossible to use one access token to query resources of multi tenants .
Please acquiring your token not from /common/oauth/v2.0/token endpoint , use /{tenent-id}/oauth2/v2.0/token endpoint with the tenant id .
Please click here for more details .

Resources