We want to achieve an authorization at our APIs.
Ex. We have API-A and API-B and both are exposed to our different consumers.
We have setup of scope based authorization in place with IdentityServer4 where we decorate endpoints with different policies. With IdentityServer4 we are able to achieve this as IdentityServer4 token has scopes claims present in all the grant types but with Azure AD, we found we can't have scope claim in token generated with Client Credential flow.
In our case, Web API B is also exposed to consumers and again they have scope based authorization. To call, Web API B from Web API A we use client credential flow and it will not have scopes claim in token so we are not able to authorize our call to Web API B.
How to achieve scope based authorization with Azure AD in microservices architecture where we call other context APIs from one context.
When you are using client credential flow and using
application permission , you get roles and not scope i.e; scp claim in the token.
Application permissions are sort of roles given to the application
itself and the scope in client credentials should be used as
api://<APP_ID>/.default . They only apply when doing client
credentials authentication, where no user is involved.
See quickstart to configure app access web-apis
Scopes are usually delegated permissions that only apply when a
user is involved in the login process. They allow you to act on
behalf of a user i.e; In the user context only, we will get
scp claims in case of client credential flow.
See azure-ad-scope-based-authorization
So , If you want delegated permissions then you will have to use implicit grant flow instead of client credentials.
As scopes in expose an api page are for Authorization Code Grant flows and where the user is involved, in this case (client credential) its not possible, we have to add our own scopes that is availible for applications to use which are indirectly called roles that we need to add in the manifest itself under approles in the app registration or through the app roles blade.
ex:
{
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Read all todo items",
"id": "f8dxxxxxxxxxxxxf98",
"isEnabled": true,
"description": "Allow the application to read all todo items as itself.",
"value": "Todo.Read.All"
}
]
}
After that , those has to be granted admin consent.
So now when requesting a token with a default scope of api://<app id>/.default the "scopes" are returned in the roles claim.
So we can use role claim for authorization purpose.
Also as a work around
Try to make sure to add additional scope like profile, offline_access open_id.
And give response_type=token instead of id_token
Example request:
......&redirect_uri=https://jwt.io&scope=openid profile offline_access&response_type=token&prompt=login
References:
Scope-based authorization in your API with Azure AD – the IT generalist (wordpress.com)
Scope is not being added to Access Token returned from Azure Ad - Stack Overflow
EDIT:
To call a web api from other , there need to be scopes defined in
one api i.e (api2 that you want to call) and those scopes need to be
selected in calling api(api1) . Please go through the process
here
When login in first Api include scope in the request and also try
response type as Token and see if scp available or then with idtoken
https://tenant.b2clogin.com/tenant.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1_TenantSignUpIn&client_id=<appid>&nonce=defaultNonce&redirect_uri=https://jwt.ms&scope=openid offline profile https://tenant.onmicrosoft.com/b2capi/write https://tenant.onmicrosoft.com/b2capi/read https://tenant.onmicrosoft.com/b2capi/user_impersonation&response_type=id_token&prompt=login.
Please note that scopes are present as roles depending on the flow
type.
Related
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:
I'm building a CLI app for provisioning Azure resources. Previously I was using the authorization code flow with the resource set to https://management.azure.com/. Now, I would like to switch to using the RFC 8628 device authorization grant type (Azure documentation). I can successfully login with scopes like openid profile. However, when I use a scope like https://management.azure.com I get an error:
{
"error": "invalid_scope",
"error_description": "AADSTS70011: The provided request must include a 'scope' input parameter. The provided value for the input parameter 'scope' is not valid. The scope openid https://management.azure.com/ is not valid. The scope format is invalid. Scope must be in a valid URI form <https://example/scope> or a valid Guid <guid/scope>.\r\n[..]",
"error_codes": [70011],
}
I'm sending a POST request with a body like client_id=<client-id>&scope=openid+https%3A%2F%2Fgraph.microsoft.com%2F.default to https://login.microsoftonline.com/<tenant>/oauth2/v2.0/devicecode. With these scopes, I can login just fine, but any subsequent requests to Azure resource management APIs (for example, to DELETE a resource group) will fail with 401 Unauthorized.
If you want to use the device code flow to access the azure resources, please follow the steps as below.
1.Navigate to your AD App in the Azure Active Directory in the portal -> API permissions -> Add a permission -> select Azure Service Management API -> select the user_impersonation.
2.Navigate to the subscription in the portal -> Access control (IAM), make sure your user account used to login has a role e.g. Contributor in the subscription. If not, please add the user as a role in the subscription, follow this doc.
3.In the postman, use the request below.
Request URL:
POST https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/devicecode
Request Body:
client_id=<client-id>
scope=https://management.azure.com/user_impersonation
In the browser, navigate to the https://microsoft.com/devicelogin, input the code and login your user account, the app will let you consent the permission, click the Accept.
4.After login successfully, in the postman, use the request below.
Request URL:
POST https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
Request Body:
grant_type: urn:ietf:params:oauth:grant-type:device_code
client_id: <client-id>
device_code: <device_code in the screenshot of step 3>
5.Use the access_token in step 4 to call Azure REST API, e.g. Resource Groups - List, it works fine.
For more details, you could refer to - Microsoft identity platform and the OAuth 2.0 device authorization grant flow.
Besides, to consent the permission successfully in step 3, make sure the setting below( Azure AD -> Enterprise applications -> User settings -> Users can consent to apps accessing company data on their behalf) in your tenant is set to Yes, otherwise, you need to let your admin click the Grant admin consent for xxxx button in step 1.
I am using Azure AD to secure my service to service calls. (Each service having an application identity in Azure AD).
Example: Application A wants to access Application B.
I noticed that when requesting an accesstoken from Application A using Client Credential Flow (with Certificate), an accesstoken is issued without having me to explicitly set the permissions to access Application B.
This seems odd to me because the token returned has its audience set to Application B even thought I haven't explicitly given it access.
If I understand correctly, all registered app have access to each other by default?
Is there a way in Azure AD to explicitly require permissions to be set in order for application to access each other?
Below is a screenshot of Application A required permissions. As you can see, Application B is not listed here.
In the following screenshot, I assigned TodoListService (aka Application B) to the required permissions of Application A
I noticed that when requesting an accesstoken from Application A using Client Credential Flow (with Certificate), an accesstoken is issued without having me to explicitly set the permissions to access Application B.
Yeah, that one can be a bit surprising and I'm not sure why that is the case either.
What you need to do is define application permissions on the API, and then assign it on the client.
Then you need to check the caller has the required app permission in the token.
I have an article on this topic: Defining permission scopes and roles offered by an app in Azure AD.
To define an app permission on the API, you'll have to edit its manifest in Azure AD, and add an app role with member type of Application, something like:
{
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Read all todo items",
"id": "f8d39977-e31e-460b-b92c-9bef51d14f98",
"isEnabled": true,
"description": "Allow the application to read all todo items as itself.",
"value": "Todo.Read.All"
}
]
}
IIRC you have to generate a GUID for the id.
After defining this permission on the API, go to your client app, and add the app permission in the Required permissions.
Then you should press Grant permissions to grant the app permission.
Now then when the client acquires a token with client credentials, the token will contain:
{
"roles": [
"Todo.Read.All"
]
}
So you'll have to check that that is present.
Our application is managing Office 365 calendars without requiring explicit consent from users using the Office 365 Exchange Online API. This works as expected for existing installations at customers, but for new customer all requests to the Exchange Online API return a 401 Unauthorized response. We've narrowed this down to a roles claim missing in the JWT token.
Our question is if this roles claim missing in the JWT is a bug, or if this is by design. Perhaps someone from the Azure AD team can share their thoughts.
How to reproduce
In Azure AD, we've created an app registration. A public key has been uploaded in order to authenticate via ADAL4j. Next to this, some application permissions have been granted to Exchange Online:
We can successfully request a access token via ADAL4j, using https://outlook.office365.com/ as the Resource Id. The JWT looks something like this (removed some irrelevant information):
{
typ: "JWT",
alg: "RS256",
},
{
aud: "https://outlook.office365.com/",
iss: "https://sts.windows.net/yyy/",
app_displayname: "Test",
appid: "app-id",
ver: "1.0"
}
As can be seen, the property roles is missing in the JWT token.
When calling the Exchange Online API (e.g. https://outlook.office365.com/api/v2.0/users/user#tenant.onmicrosoft.com/calendars), sending the JWT as a Bearer token, a 401 Unauthorized is returned. The x-ms-diagnostics header mentions:
2000008;reason="The token contains no permissions, or permissions can not be understood.";error_category="invalid_grant"
Expected behaviour
When using an old application registration (created using the Azure Classic Portal, if I recall correctly), the JWT does contain a Roles property with the role we've requested:
{
typ: "JWT",
alg: "RS256",
},
{
aud: "https://outlook.office365.com/",
iss: "https://sts.windows.net/yyy/",
app_displayname: "Test",
appid: "app-id",
roles: [
"Calendars.ReadWrite.All"
],
ver: "1.0"
}
Using this JWT as a Bearer token when calling the Exchange Online API works as expected.
Workaround
We've worked around the issue by using the Grant Permissions button for the new app registration:
Now, the Calendars.ReadWrite.All role is present in the JWT, so everything is working as expected.
Question
In the past we've never had to execute the Grant Permissions action. Also, this page mentions (emphasis added):
As an administrator, you can also consent to an application's
delegated permissions on behalf of all the users in your tenant.
Administrative consent prevents the consent dialog from appearing for
every user in the tenant, and can be done in the Azure portal by users
with the administrator role. From the Settings page for your
application, click Required Permissions and click on the Grant
Permissions button
However, the "Read and write calendars in all mailboxes" permission is an application permission, and not a delegated permission, as mentioned at this page.
Is the workaround the correct solution to our missing Roles claim issue, or is something else wrong on the Azure AD side?
The workaround is the correct solution. When your application needs application permissions, an admin must consent by either clicking in the "grant permissions" button (as you did) or by passing admin_consent to the login URL. This applies to the AAD v1 application model. For the AAD v2 application model, there is a different way to get admin consent. More information here.
In the past (Azure Classic Portal), when you added application permissions to an application, the consent was granted automatically. This is not the case in the new Azure Portal.
I'm using the Azure AD Basic tier with an ASP.NET Core API, I've followed the RBAC sample. I've set up an application with roles in my manifest like so:
appRoles": [
{
"allowedMemberTypes": [ "User" ],
"displayName": "Read Device",
"id": "b2e6f6c2-c3d5-4721-ad49-0eea255ccf45",
"isEnabled": true,
"description": "Can read a device.",
"value": "read_device"
},
...
]
I've setup my API to use the UseJwtBearerAuthentication middleware like so:
application.UseJwtBearerAuthentication(
new JwtBearerOptions()
{
AuthenticationScheme = "Azure Active Directory",
Authority = options.Authority,
Audience = options.ClientId,
TokenValidationParameters = new TokenValidationParameters()
{
RoleClaimType = "roles",
ValidateIssuer = false
}
})
I've given my user the above 'Read Device' role:
I'm using Swagger UI to make the call to get the auth token. It calls the following URL:
https://login.microsoftonline.com/[Tenant].onmicrosoft.com/oauth2/authorize?
response_type=token
&redirect_uri=http%3A%2F%2Flocalhost%3A5100%2Fswagger%2Fo2c.html
&realm=-
&client_id=[Client ID]
&scope=http%3A%2F%2Fschemas.microsoft.com%2Fws%2F2008%2F06%2Fidentity%2Fclaims%2Frole
&state=oauth2
&resource=[Client ID]
I suspected that I am not passing the correct values to the scope parameter, so I have tried asking for every scope I can think of:
&scope=openid
%20email
%20profile
%20offline_access
%20user_impersonation
%20roles
%20http%3A%2F%2Fschemas.microsoft.com%2Fws%2F2008%2F06%2Fidentity%2Fclaims%2Frole
%20read_device
If I set "groupMembershipClaims": "All" in my manifest I can see group claims but I want roles instead. I'm able to login to call my API, however I never get any roles back in my JWT token, so I'm unable check the users role. What am I doing wrong?
It turns out I needed to request an id_token instead of a token. An id_token contains extra claims/scopes/resources about the user. I also needed to provide a nonce parameter containing a new random GUID on every request. Thus, I ended up with the following URL:
https://login.microsoftonline.com/[Tenant].onmicrosoft.com/oauth2/authorize?
response_type=id_token
&client_id=[Client ID]
&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F
&nonce=9ac5ad8d-df44-48e6-9bd6-e72743b3625c
If you are want to enable the role be assigned to users or groups(allowedMemberTypes=User) :
If you want to perform authorization using role claims , you could
follow the steps in this code sample , you could find the roles
claim is in the id_token .
If you want to make a client app to call your web api , when user
sign in ,app could check the access rules based on the role
claim,
you could use delegate flow(OAuth Authorization Code Grant,Implicit
Grant Flow..),roles claim is in the access_token ;
If you want to specify the role be assigned to client applications(allowedMemberTypes=Application), you could use OAuth Client Credential Flow ,appRoles of resource app/api that are assigned to the client app, and you will find the roles claim in the access_token ,check the detail steps from here.
Please click here for more details .
In my case I had mistakenly configured the App Registration to emit Security Groups as roles claims, thus overwriting the App Roles from the manifest. Removing the optional groups claim and logging back in correctly emitted the App Roles names in the roles claim of the id_token.