Why aren't the application permissions being added to my MS Graph token? - azure

I'm trying to get a subscription created with the callRecord resource (https://learn.microsoft.com/en-us/graph/api/subscription-post-subscriptions?view=graph-rest-beta&tabs=http)
In the app registration section of the Azure portal, I've created a multi-tenant app with a client secret. That app has permissions for application-level "CallRecords.Read.All" as well as the default delegated "User.Read". The statuses also have a green checkbox for being granted against my organization by an admin.
I am able to get an access token with the following HTTP POST request to https://login.microsoftonline.com/common/oauth2/v2.0/token:
grant_type:authorization_code
scope:https://graph.microsoft.com/.default
client_secret:<client_secret>
client_id:<client_id>
code:<code>
redirect_uri:http://localhost:3000
However, that token is not able to generate a subscription to my callRecord resource. I get a response with this message: "Operation: Create; Exception: [Status Code: Forbidden; Reason: The request is not authorized for this user or application.]"
The message suggests that the app has not been granted admin-level authorization, but in fact it has. This used to work for me. I'm wondering if there has been a regression on the MS Graph side.
Further, when I examine the JWT, I see that the scope is "User.Read profile openid email". There is no mention of the application-level permission (specifically, CallRecords.Read.All)
Thanks.

Because when you use the auth code flow, just the Delegated permission will take effect. So even if you grant the Application permission, the token you got will not include the permission.
From the doc, to call this API Get callRecord, just the Application permission is supported.
To get the token which include the permission, your option is to use the client credential flow.
Note: You need to use <tenant-id> instead of common in this flow.
POST https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
client_id=xxxxxxx
&scope=https://graph.microsoft.com/.default
&client_secret=xxxxxxx
&grant_type=client_credentials
Decode the token in https://jwt.io, the roles includes the CallRecords.Read.All permission:

Related

For IMAP.AccessAsUser.All Scope ADSTS65001: The user or administrator has not consented to use the application

In my java web application I want to get access to user's mailbox by using jakarta mail. For that purpose I followed https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth for OAuth2 authorization code flow.
On Azure port I setup my app and added API permissions as below
Now user is redirecting to below authorize endpoint:
https://login.microsoftonline.com/5426ee07-9b73-4a9e-8075-395ab439c6fa/oauth2/v2.0/authorize?client_id=b6067ad9-7195-430b-a35d-97b7aa7beb8f&response_type=code&redirect_uri=http://localhost:8080/callback/microsoft&response_mode=query&scope=offline_access%20https%3A%2F%2Fgraph.microsoft.com%2FIMAP.AccessAsUser.All%20https%3A%2F%2Fgraph.microsoft.com%2FSMTP.Send
After entering credentials and accepting the consent redirect_uri gets hit with auth code. Based on that auth code I formed token endpoint URL and hitting it from server, the token endpoint is as follow:
URL: https://login.microsoftonline.com/5426ee07-9b73-4a9e-8075-395ab439c6fa/oauth2/v2.0/token
Form Data:
client_id=b6067ad9-7195-430b-a35d-97b7aa7beb8f
scope=offline_access%20https%3A%2F%2Foutlook.office.com%2FIMAP.AccessAsUser.All
redirect_uri=http://localhost:8080/callback/microsoft
grant_type=authorization_code
client_secret=QUs8Q~aboLBiopTezMTKwzQjIwWsFFXjc2kCRaRs (I know I have shared the secret)
code={code received from authorize end point}
Response to this post request comes as:
{"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID 'b6067ad9-7195-430b-a35d-97b7aa7beb8f' named 'Email Connector'. Send an interactive authorization request for this user and resource.\r\nTrace ID: dc008ced-e23f-4919-bd45-b7ae7c68b000\r\nCorrelation ID: 9b6ede03-3c05-4a78-8975-036a3cb20773\r\nTimestamp: 2022-06-07 19:51:30Z","error_codes":[65001],"timestamp":"2022-06-07 19:51:30Z","trace_id":"dc008ced-e23f-4919-bd45-b7ae7c68b000","correlation_id":"9b6ede03-3c05-4a78-8975-036a3cb20773","suberror":"consent_required"}
Here, I don't understand why the error is saying The user or administrator has not consented to use the application, user has accepted the consent after entering credentials on authorize end point. Event more If we look at the screenshot above admin has already given grant to access the directory.
I tried to reproduce the same scenario in my environment and got the same error as below:
To resolve the error, please check the authorize endpoint you are using to get the code.
Avoid using Microsoft graph API scopes while getting the code.
Replace it with the scope you are using to get access token like below:
https://login.microsoftonline.com/Your_TenantID/oauth2/v2.0/authorize?
client_id=Your_ClientID
&response_type=code
&redirect_uri=http://localhost:8080/callback/microsoft
&response_mode=query
&scope= offline_access https://outlook.office.com/IMAP.AccessAsUser.All
&state=12345
Get the code from the above authorization endpoint.
I got the access token successfully after modifying the endpoint like below:
To validate the access token decode it in jwt.io and check the aud and scp claims like below:

Get Azure Webjob History - 403 Token invalid

I am trying to retrieve the web job history of an Azure web job via REST using a .NET backend and the OAuth2 credentials flow (as described here
https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/get-triggered-web-job-history-slot)
How do I need to authenticate correctly?
I retrieve the token as follows:
POST https://login.microsoftonline.com/{MySubscription}/oauth2/v2.0/token
client_id={MyApp}
&grant_type=client_credentials
&scope=https://management.azure.com/.default
&client_secret={myclient_secret}
I get a token back, however I get a 403 error message when I try to retrieve the resource:
GET https://management.azure.com/subscriptions/{MySubscription}/resourceGroups/{MyResource}/providers/Microsoft.Web/sites/{MyApp}/slots/{MySlot}/triggeredwebjobs/{MyWebjob}/history?api-version=2021-02-01
Authorization: Bearer {MyToken}
Client '{MyApp}' with object ID '{MyApp}' is not
authorized to perform the action
'Microsoft.Web/sites/slots/triggeredwebjobs/history/read' using the
scope
'/subscriptions/{MySubscription}/resourceGroups/{MyResource}/providers/Microsoft.Web/sites/{MyApp}/slots/{MySlot}/triggeredwebjobs/{MyWebjob}'
or the scope is invalid. If access was granted recently, please update
your credentials.
What am I doing wrong?
I already added the API-Permission
The "403 Token invalid" error usually occurs if you missed giving permissions to particular scope (Azure Service Management).
By giving this scope it enables you to access https://management.azure.com
To resolve this error, please follow below steps:
Go to Azure Ad ->your application -> API permissions -> Add permission -> Azure Service Management -> delegated permissions ->User impersonation -> Add
After giving these permissions try to retrieve the resource again, there won't be any error.
Since I didn't find a solution that worked with OAuth2 and the Credentials flow, I got it working with Basic Authentication. The username (userName) and password (userPWD) can be taken from the publishing profile of the respective app service.
GET https://{appservicename}.scm.azurewebsites.net/api/triggeredwebjobs/{jobName}/history
Authorization Basic ....

Why doesn't Azure REST API to assign user role to a subscription work?

I am trying to add a user role to a subscription in Azure using REST API following this documentation.
I got a bearer token with my login and passed it as a header Authorization parameter.
Gave all the values as described in the doc
PFA REST API call I performed.
API response says below;
{
"error": {
"code": "AuthenticationFailed",
"message": "Authentication failed."
}
}
Please let me know if any one successfully used this API and performed operations. Also is there any Azure .NET API to this operation?
REST API Postman request
The error means your user account does not have the permission to create the role assignment, specifically Microsoft.Authorization/roleAssignments/write.
To solve the issue, you need to ask the admin who is the Owner or User Access Administrator(or custom RBAC role with Microsoft.Authorization/roleAssignments/write permission) of your subscription to assign the Owner or User Access Administrator or custom role with the permission above for you at the subscription scope first, follow this link, then get a new token, you will be able to assign the role to others like the admin assign the role to you i.e. create role assignment.
Update:
If you want to get the access token via your user credential, you could use the auth code flow, please follow the steps below.
1.In your App registration, add the user_impersonation Delegated permission of Azure Service Management API.
2.Hit the URL below in the browser, change the tenant-id, client-id, redirect_uri to yours, login your user account.
https://login.microsoftonline.com/tenant-id/oauth2/v2.0/authorize?
client_id=xxxxxxxx
&response_type=code
&redirect_uri=http://localhost
&response_mode=query
&scope=https://management.azure.com/.default
&state=12345
Then you will get a code like below, copy it.
Don't forget to remove the state and session_state.
3.In the postman, use the query below, then you can get the token.

Authenticate for Azure REST API without login

I have a backend process that doesn't directly interact with the user. I want to access reservations associated with my Azure account but I'm having trouble with the authentication step. I was following the guide here and I managed to get the authentication request to work by calling
https://login.microsoftonline.com/{tenant-ID}/oauth2/token
as a POST with the following x-www-form-urlencoded body:
grant_type=client_credentials&
client_id={client-ID}&
client_secret={client-Secret}&
resource=http://myapp42
However, when I attempt to call:
https://management.azure.com/providers/Microsoft.Capacity/reservationOrders/{order-ID}/reservations/{reservation-ID}?api-version=2019-04-01
with the bearer token I received during the authentication step, I get the following error message:
The access token has been obtained for wrong audience or resource 'http://myapp42'. It should exactly match with one of the allowed audiences 'https://management.core.windows.net/', 'https://management.core.windows.net', 'https://management.azure.com/', 'https://management.azure.com'
However, if I modify the resource on the request to be one of these, http://management.core.windows.net/ for instance, the authentication then fails with:
AADSTS500011: The resource principal named https%3A%2F%2Fmanagement.core.windows.net%2F was not found in the tenant named {tenant-ID}. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.\r\nTrace ID: b54cedea-3804-41cf-bd27-fcf0ed1c4700\r\nCorrelation ID: 2371d375-6c89-4f05-83c9-c4629b3340a8\r\nTimestamp: 2020-02-05 01:59:57Z
How do I authenticate so that I can then get my reservations without having to login?
Update:
The service principal has both the Owner and Contributor roles assigned.
Update 2:
Thanks to #Jim Xu, I was realized that I needed to refrain from url-encoding the URL. That allowed me to get the access token with a value of https://management.azure.com/ for the resource field. However, at this point, when I attempt to call the REST API with the resulting bearer token, I get the following error:
The client '{Object-ID}' with object id '{Object-ID}' does not have authorization to perform action 'Microsoft.Capacity/reservationOrders/reservations/read' over scope '/providers/Microsoft.Capacity/reservationOrders/{order-ID}/reservations/{reservation-ID}' or the scope is invalid. If access was recently granted, please refresh your credentials
Note: The object ID returned by this error is the one associated with the service principal.
Update 3:
I checked the reservation and it seems that the principal does not have a role in that reservation's access control. However, I also cannot assign the principal a role because it does not show when I search for principals during the role-assigning process.
If you want to call Azure Rest API to get the information of reservation Orders, you need to assign Owner\Contributor for the service principal.(the action needs Microsoft.Capacity/reservationOrders/read permission).
The steps are as below
Get access token
POST : https://login.microsoftonline.com/{tenant-ID}/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id={client-ID}&
client_secret={client-Secret}&
resource=https://management.azure.com/ or https://management.core.windows.net
Call the api
GET : https://management.azure.com/providers/Microsoft.Capacity/reservationOrders/{order-ID}/reservations/{reservation-ID}?api-version=2019-04-01
Authorization: Bearer <token>
For more details, please refer to the issue and the issue
update
If you have assigned role but you still cannot get access token, please try to encode your url.

Wrong access_token from AAD with OAuth2 flow

I am making OAuth 2.0 auth code authentication flow with multi-tenant application.
Here is my authorize url:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=my_id&prompt=consent&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauthorize&response_type=code&scope=openid+offline_access&state=17
It goes fine and I receive auth_code. Then I make request with this auth_code to token_url and receive a lot of information, like:
token_type
scope
id_token
access_token
refresh_token
expires_at
ext_expires_in
Seems fine to me, but when I make request on API with access_token like:
https://management.azure.com/subscriptions/my_sub_id/locations?api-version=2016-06-01
with headers:
Content-Type:
- application/json
Authorization:
- Bearer EwBQA8l6BAAURSN/FHlDW5xN74t6GzbtsBBeBUYAAV1IHgHb4dOWblzfd/YsSuFicAMDYbua17QivnAT9/pIaeKAg3uKsK5VGqWLzjMOUQrCpd7R1RAM6RkzI0u8e4rpO7DISG7qLso5H5+U1jb+38/j1urcwlXMMxhy83ZXmdpkLXpZV+vcOV...
It responds with 401 error
body:
encoding: UTF-8
string: '{"error":{"code":"InvalidAuthenticationToken","message":"The access token is invalid."}}'
To be honest I think something wrong with my access_token. It seems not like JWT for me. Documentation says it looks like:
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCEV1Q..."
But my access_token looks like:
"access_token": "EwBYA8l6BAAURSN/FHlDW5xN74t6GzbtsBBeBUYAAZDe7JE/MPLoAi+Fr+1Xxq5eBe5N9l8Q+c4QjkY5PGEzRnBpPe7+v6h+PLdh1cceBQx+/JsB2QCrYSCt7x/zGsQAhwoY/"
Is it fine?
Here is my permissions for application:
Permissions
The main issue you have here is that you have only asked for an access token for the scopes openid offline_access. The resulting access token will be for Microsoft Graph (https://graph.microsoft.com), not for the Azure REST API (https://management.azure.com).
To indicate you would like a token for a given API, the scope parameter in your authorization request should include the delegated permission you would like the app to have for the API. In the case of Azure REST API, there's only one delegated permission: user_impersonation. The identifier URI for the Azure REST API is https://management.azure.com, so the scope value you want to use is:
openid offline_access https://management.azure.com/user_impersonation
Two more important notes:
As you've discovered, you will not always be issued an access token as a JWT which you can decode peek at. The format of the access token is an agreement between the service which issued the token (Azure AD or Microsoft Accounts, in this case), and the service for which the token was issued (Microsoft Graph, in this example).
You should not always include prompt=consent. prompt=consent should only be used if you have already tried signing in the user without the user needs to be re-prompted for consent for a new permission.
If you simply include the required scopes in the scopes parameter, the Microsoft Identity platform will take care of figuring out if it needs to prompt for consent or not. If you always include prompt=consent, you will find that many organizations will be blocked from accessing your app, because they've disabled the ability for users to grant consent themselves (and this parameter specifically states that you require the user to be prompted again).

Resources