I have a Blazor WASM Azure static web app that communicates with an Azure AD protected API running on ASP.NET Core. I had setup Microsoft Account and one-time passcode IDPs.
I could sign in and call protected endpoints on my API - only when signed in - until I had my first Microsoft Account that wasn't in the tenant. They got AADSTS50020. The only cause that applied to me was cause 2 (used the wrong endpoint), so I changed my authority from https://login.microsoftonline.com/<YourTenantNameOrID> to https://login.microsoftonline.com/common according to that help page and this documentation.
Now every user can sign but we all get 401 unauthorized on protected endpoints.
The WWW-Authenticate header is set to Bearer error="invalid_token", error_description="The signature is invalid".
I obviously haven't "tried everything" if one simple change causes everything to break, but I've pored over documentation these past couple days to no avail.
Notes:
The one-time passcode sign-in option went away, and my main MS account is never auto-suggested even though I use it every time.
My tokens "look" fine, the aud is the API client id and the 1 necessary scope is present in scp.
API manifest
...
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": false,
...
"oauth2Permissions": [
{
"adminConsentDescription": "Allows the app to access the web API on behalf of the signed-in user",
"adminConsentDisplayName": "Access the API on behalf of a user",
"id": "<access_as_user scope id>",
"isEnabled": true,
"lang": null,
"origin": "Application",
"type": "User",
"userConsentDescription": "Allows this app to access the web API on your behalf",
"userConsentDisplayName": "Access the API on your behalf",
"value": "access_as_user"
}
],
...
"signInAudience": "AzureADandPersonalMicrosoftAccount",
...
Client manifest
...
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
...
"requiredResourceAccess": [
...
{
"resourceAppId": "<api client id>",
"resourceAccess": [
{
"id": "<access_as_user scope id>",
"type": "Scope"
}
]
}
],
...
"signInAudience": "AzureADandPersonalMicrosoftAccount",
...
I can provide additional information, such as configuration code, but I didn't want to start out making the question too long.
Well, the rule of finding the answer shortly after posting on a help board relative to the time spent searching for the answer before posting on the help board reigns supreme.
For anyone who has the same question, my TenantId configuration on my API was set to my actual tenant id. Even if your client requests a token from the common endpoint, your API needs tenant set to common too.
See more:
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-configuration
Related
In my scenario there is a Azure Application Registration (client_app) with credentials. This application is used to request an oauth2 access token. A second Application Registration (main_app) is the scope, which is providing App Roles and more.
My goal is to include information from client_app in a jwt token claim, when requesting the token using the client credential flow on an azure /oauth2/v2.0/token endpoint.
This is a token request:
POST /<tenant id>/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=<Application ID of client_app>
&client_secret=<secret of client_app>
&scope=<Application ID URI of main_app + "/.default">
&grant_type=client_credentials
The response of the token request is a valid token including roles (granted App Roles from main_app to client_app), aud, sub and idtype: app claims. But my optional claims are missing.
MS docs state, that it is possible to include optional claims using directory extensions
Schema and open extensions are not supported by optional claims, only the AAD-Graph style directory extensions. This feature is useful for attaching additional user information that your app can use [...]
So I tried adding a directory extension to my main_app (extension_<main_app ID>_someAttrName), added an optional claim to main_app and stored a value to this directory extension in client_app. Unfortunately the extension value is not reflected in the token.
I tried adding the directory extension on the client_app itself (extension_<client_app ID>_someAttrName) and map the directory extension to main_app by using ClaimsMappingPolicy. Unfortunately the extension value is not reflected in the token.
It seems to me, that the claims are missing, since they do not originate from a user object. MS docs provide a lot of information to add optional claims, but most scenarios involve a user. E.g. the optional claims source property of a manifest:
The source (directory object) of the claim. There are predefined claims and user-defined claims from extension properties. If the source value is null, the claim is a predefined optional claim. If the source value is user, the value in the name property is the extension property from the user object.
As I understand it, only null and "user" is supported. I want to include a directory extension originating from an Application Registration (client_app). And since I have to use the client credential flow (server-to-server) there is no user involvement.
So how do I add custom optional claims to a token when using client credential flow, when no user object is involved? There is an application object involved. So how do I need to configure claims to reflect custom application data (e.g. directory extensions)?
Manifest of main_app:
{
"id": "<main_app ID>",
"acceptMappedClaims": true,
"accessTokenAcceptedVersion": 2,
"appId": "<main_app APP ID>",
"appRoles": [{
"allowedMemberTypes": ["Application"],
"isEnabled": true,
"origin": "Application",
"value": "readAll"
},{
"allowedMemberTypes": ["Application"],
"isEnabled": true,
"origin": "Application",
"value": "writeAll"
}],
"identifierUris": ["api://<main_app ID>"],
"optionalClaims": {
"idToken": [],
"accessToken": [ {
"name": "extension_<main_app ID>_someAttrName",
"source": "application", <-- propably invalid, "user" or null is supported
"essential": false,
"additionalProperties": []
}, { ... }
],
},
} /# trunced for readability
Manifest of client_app:
{
"id": "<client_app ID>",
"accessTokenAcceptedVersion": null,
"appId": "<client_app APP ID>",
"passwordCredentials": [{...}],
"extension_<main_app ID>_someAttrName": "some-string-value"
} /# trunced for readability
My ClaimsMappingPolicy definition looks like this:
"ClaimsMappingPolicy": { "Version": 1, "IncludeBasicClaimSet": "true",
"ClaimsSchema": [ {
"Source": "application",
"ExtensionID": "extension_<client_app ID>_someAttrName",
"JwtClaimType": "someAttrName"
} ]
}
I reached out to Azure support engineers, and they told me, that it's not possible to include directory extension based claims originating from Application Registrations. They did not go into detail but replied this:
I apologize to inform we got to know that reflecting extension claims in JWT token won't be possible. Since directory extensions can't be added to servicePrincipal objects.
I'm assuming the requesting entity is a service principal when using the client credentials flow.
I am using OAuth2 Code flow and trying to get the user's groups back in my access token. Everything I'm reading says I should see either a groups claim or a hasgroups claim, but I see neither.
I've altered the following fields in the App registration manifest for my client app:
"groupMembershipClaims": "SecurityGroup",
"optionalClaims": {
"idToken": [],
"accessToken": [
{
"name": "groups",
"source": null,
"essential": false,
"additionalProperties": [
"dns_domain_and_sam_account_name"
]
}
],
"saml2Token": []
},
Here is an example of my querystring for the login redirection url (login.microsoftonline.com)...
client_id=<clientId>
&response_type=code
&redirect_uri=<redirectUri>
&response_mode=query
&scope=<appScope>%20offline_access
&state=67890
Here is an example of my querystring for my requesting the token using authCode (login.microsoftonline.com/{tenantId}/oauth2/v2.0/token)
client_id=<clientId>
&scope=<uriEncodedScopes>%20offline_access
&redirect_uri=<uriEncodedRedirectUri>
&code=<authCode>
&client_secret=<uriEncodedClientSecret>
&grant_type=authorization_code
Everything is working great, but I can't figure out how to get groups info back in my token.
UPDATE
I added %20openid to my scope in both urls, and now I'm getting an id_token, but I still don't see "groups" or "hasgroups" in either token.
UPDATE
I just added the same manifest changes (groupMembership, optionalClaims) to my API App Registration (instead of my client) - the API that exposes the scope, and I see no change whatsoever. Access token and Id token don't have any reference to groups at all.
Per my test, it should work. And you just need to configure the groupMembershipClaims, optionalClaims in your API App Registration, refer to the reason below.
You could refer to my test sample below, I tried with a work account or personal account, both work.
Request the authorization code(the api://3e013fde-xxxxxxa422f3/User.Test is my API permission):
https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize?
client_id=xxxxxxxxxxxxxxx
&response_type=code
&redirect_uri=https://localhost
&response_mode=query
&scope=openid offline_access api://3e013fde-xxxxxxa422f3/User.Test
&state=12345
Request the token:
Decode the token in https://jwt.io/, the groups claim is included.
Note:
My client App and API App are both created today, I suppose there are may some difference between the app created in the old App Registration(it is not existing in the portal currently), app registration portal(it has been moved to the new App Registrations), new App Registration(theApp Registrationin portal currently).
And from this doc:
There is also a weird thing during my test, when I create a new API App Registration, just set "groupMembershipClaims": "SecurityGroup" without setting optionalClaims, the manifest will be like below.
"groupMembershipClaims": "SecurityGroup",
"optionalClaims": {
"idToken": [],
"accessToken": [],
"saml2Token": []
}
Then the Access token will not include groups, the ID token has the groups.
If I set it with yours, the Access token will have the groups.
"groupMembershipClaims": "SecurityGroup",
"optionalClaims": {
"idToken": [],
"accessToken": [
{
"name": "groups",
"source": null,
"essential": false,
"additionalProperties": [
"dns_domain_and_sam_account_name"
]
}
],
"saml2Token": []
}
But when I set it return to
"groupMembershipClaims": "SecurityGroup",
"optionalClaims": {
"idToken": [],
"accessToken": [],
"saml2Token": []
}
The Access token still has the groups.
From the portal - Token configuration (preview) and this doc - Configure group claims for applications with Azure Active Directory (Public Preview), the feature should be in preview, it may be a bug(I am not sure).
So in conclusion, I recommend you to use two new Apps to have a try.
I have an ASP.NET Core API hosted in Azure which is to be accessed by several trusted clients. I wish to offer an /auth endpoint which accepts a client_id and client_secret. Response will be an OAuth access token with expiry.
The many examples/tutorials I have found mostly relate to username/password login and full OAuth flow (B2C) which isn't what I'm looking for as the trusted clients have the secret.
I've been looking at Azure API Management which links through to Azure AD for OAuth but I'm thinking this is just complicating things right now.
In the past I have generated and validated JWT bearer tokens using the ASP.NET middleware, but I am sure I should be generating and validating tokens via Azure AD - or am I wrong here?
[expecting to get some down votes for not being explicitly code related, but really need a little bit of advice to get me past this]
I wish to offer an /auth endpoint which accepts a client_id and
client_secret. Response will be an OAuth access token with expiry.
You can use client credential flow. Then you can use this endpoint to get access token.
POST /{tenant}/oauth2/v2.0/token HTTP/1.1 //Line breaks for clarity
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=535fb089-9ff3-47b6-9bfb-4f1264799865
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_secret=qWgdYAmab0YSkuL1qKv5bPX
&grant_type=client_credentials
Before doing this, you need to expose application permissions of your application api. The contents of appRoles are as below.
"appRoles": [
{
"allowedMemberTypes": [ "Application" ],
"description": "Accesses the TodoListService-Cert as an application.",
"displayName": "access_as_application",
"id": "ccf784a6-fd0c-45f2-9c08-2f9d162a0628",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "access_as_application"
}
],
I would like to change the price tier of my azure analysis service via rest API in logic APP.
To do this job I have founded this API
In logic APP I have set such a request
{
"uri": "https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.AnalysisServices/servers/{serverName}?api-version=2017-08-01",
"method": "PATCH",
"authentication": {
"tenant": "GUID, which I get from Azure AD for AzureAS",
"audience": "https://management.azure.windows.net",
"clientId": "GUID, which I get from AzureAD for AzureAS",
"secret": "*sanitized*",
"type": "ActiveDirectoryOAuth"
},
"body": {
"sku": {
"capacity": 1,
"name": "S4",
"tier": "Standard"
},
"tags": {
"testKey": "testValue"
}
}
}
After sending this request I get this message:
BadRequest. Http request failed as there is an error getting AD OAuth token: 'AADSTS50001: The application named https://management.azure.windows.net was not found in the tenant named xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. 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.
In AzureAD I see the following:
My question is now for this API which API-Permission should I add in AzureAD?
As mentioned in the comments, the resource (audience) for which the code/token should be acquired is https://management.core.windows.net/.
Moreover you need to have following delegated permission to execute Azure API: Execute Windows Azure Service Management API.
Once you do that, you should be able to perform the operation.
I'm trying to overload the OAuth ID Token by adding additional claims. I can already use the Graph API to get the claims that I need but I would like to understand if it's possible to add the additional claims directly into the ID Token? I've updated the manifest by adding the required claims in and then flipping "acceptMappedClaims" to true, however I still don't see these in the ID token. What am I missing?
"optionalClaims": {
"idToken": [
{
"name": "employeeid",
"source": "user",
"essential": true,
"additionalProperties": []
},
{
"name": "mail",
"source": "user",
"essential": true,
"additionalProperties": []
}
],
"accessToken": [],
"saml2Token": []
},
"acceptMappedClaims": true,
This depends on where the ID token is generated from. If it's on-premises AD and federated identity is used take a look at Customizing the OIDC id_token in ADFS 2016.
If it's just a cloud identity I'd take a look at the second link jwmiller5 posted or this one: how-to-set-claims-from-asp-net-openid-connect-owin-components.
Hope this helps,
Bernie
If you are trying to add additional claims into your AD token, you would need Azure AD premium and you can add the values as attributes. See Claim augmentation with Azure AD authentcation
If you just need the claims in one particular application, you can add the claims in the app itself. See Azure AD PostAuthentication add claims