"AADSTS65001: The user or administrator has not consented to use the application" but the Admin has consented - azure

I am adapting the project sample provided by Microsoft for Multi-tenant Azure AD apps.
I am extending SurveyAuthenticationEvents.TokenValidated() so that in the sign up logic, I hit up Microsoft Graph to get the tenant display name so we can store something a little more meaningful than just a GUID to identify the new tenant.
Something like this:
Organization? org = default;
var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
var auth = await tokenAcquisition.GetAuthenticationResultForUserAsync(new string[] { "User.Read" }, tenantId: azureTenantId, user: context.Principal); // "User.Read", "Organization.Read.All"
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", auth.AccessToken); // context.SecurityToken.RawData);
return Task.CompletedTask;
}));
var results = await graphClient.Organization.Request().Select(x =>x.DisplayName).GetAsync();
org = results.FirstOrDefault();
The third line is failing with the exception:
Microsoft.Identity.Client.MsalUiRequiredException: AADSTS65001: The user or administrator has not consented to use the application with ID 'xxxxxx' named 'xxxxx'. Send an interactive authorization request for this user and resource.
Please note that this is IMMEDIATELY after the tenant administrator has just consented.
However, the error seems to be intermittent. In fact if I debug and break on the problematic line, wait a couple of seconds and then let it run it works fine. It is as if Azure AD needs a second or two after the consent to properly update the service principals and consent status for the new tenant, before it will issue an access token for a downstream API.
Am I missing something here or do I just add some retries and work around the issue?

If an admin consent is already believed to be done , maybe all of the required permissions listed in the sign-in request were not consented to
or
the wrong application was used based on the App-Id}from the table above.
In that case try to add this as an authorized client application
Once the application has been consented ,please make sure the prompt parameter is not being specified. If prompt parameter is still passed after consent this error might occur
For workaround delete the package , permissions and again add it so your permission request gets created again.Also check if you need additional permissions like openid , profile ,offline_access for refresh token in your case.
Please check other possible causes here
Troubleshooting consent in Azure AD | Azure Active Directory Developer Support Team (aaddevsup.xyz) which can guide to troubleshoot
Reference:
Unexpected consent prompt when signing in to an application - Microsoft Entra | Microsoft Docs

Based on some feedback on github (https://github.com/mspnp/multitenant-saas-guidance/issues/127) it appears that the issue is indeed due to timing issues with AzureAD infrastructure, possibly related to caching.
Would be fantastic if this was documented!
I have now introduced some retry logic that simply waits a few seconds and retries the same request (up to 5 times) and the sign up logic now works as expected.
Thanks to #dmcsweeney on github

Related

Change Azure B2C account password on behalf of a user

I'm trying to change the password of a user created in my Azure B2C Tenant. I'm using Microsoft.Graph C# SDK to do API calls.
First I create GraphServiceClient by providing details of my tenant:
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential("tenantId", "clientId", "clientSecret", options);
_client = new GraphServiceClient(clientSecretCredential);
Then I use the built-in method of a client to change the user's password:
await _client.Users["userId"].ChangePassword(currentPassword, newPassword).Request().PostAsync();
But I get following error:
Microsoft.Graph.ServiceException: Code: Authorization_RequestDenied
Message: Access to change password operation is denied.
Is it possible to change the password on behalf of a user? I found conflicting information on this topic on the internet. If this is impossible, what is the valid approach to do this?
I have a SPA frontend app in which the user is authenticated with redirection flow. The access token is then passed to my backend to authorize the requests. Maybe I can use this token to somehow access MS Graph API and change the password?
Note: I would like to let users change their passwords without leaving my SPA application.
I tried in my environment and got below results:
Initially I tried with your code and got same error:
According to MS-DOCS change password we need Directory.AccessAsUser.All permission and it only supported for delegated (Work or school accounts).
Trying to reset the password in the B2C tenant using the Graph API its is not possible Unfortunately, because there is no Directory.AccessAsUser.All, B2C tenant does not allow this type of password reset. For B2C tenants, the Graph API includes the permission. Only offline access and openID are available as delegated rights in the B2C tenant.
You can reset the password in B2C tenant are either admin performing password reset via Azure Portal or by using Password Reset Policy and also refer this MS-Q&A for updating password in powershell and postman.
Reference:
"Upn from claims with value null is not a valid upn." - Microsoft Q&A by amanpreetsingh-msft.

Azure WVD Rest API Auth

I have a aspnetcore app that I'm writing and would like to be able to manage WVD resources. The problem I'm having is that the Bearer token I'm getting from Msal is giving me a 401 when I try to
GET https://rdweb.wvd.microsoft.com/api/feeddiscovery/webfeeddiscovery.aspx
I thought maybe I needed to add an API permission to my app in azure, but I've already added:
https://management.azure.com/user_impersonation
And I cant seem to locate anything that suggests it might work for WVD.
Maybe I'm way off track though.
I've tried looking at the source:
https://github.com/Azure/RDS-Templates/tree/master/wvd-templates/wvd-management-ux/deploy
But its been compiled and minified, so thats proving to be difficult.
Any help getting a valid token to call the WVD Rest API would be greatly appreciated.
Getting the token:
Full Code (minus the Microsoft.Identity.Web stuff)
var token = await TokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new[] { "https://mrs-Prod.ame.gbl/mrs-RDInfra-prod/user_impersonation" });
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://rdweb.wvd.microsoft.com/");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", $"{token}");
var result = await httpClient.GetAsync("api/hubdiscovery/eventhubdiscovery.aspx");
result = await httpClient.GetAsync("api/feeddiscovery/webfeeddiscovery.aspx");
This method is from the Microsoft.Identity.Web project.
The https://management.azure.com is for Azure Service Management API, in your case, it is not correct.
Please navigate to the AD App in the portal -> API permissions -> APIs my organization uses -> search by Windows Virtual Desktop, find it and click.
If you want the management tool to make Windows Virtual Desktop management calls on behalf of the user who's signed into the tool, choose Delegated permissions -> user_impersonation, complete the steps like the screenshot. You can also let the user consent the permission by himself without clicking the Grant admin consent button, it depends on you.
Then the permission appears like below.
For more details, see this Tutorial: Deploy a management tool and this step.
Update:
Try to use powershell New-RdsRoleAssignment to add user account as a RDS Owner role, make sure you have installed the Microsoft.RDInfra.RDPowerShell module first, refer to this link.
Add-RdsAccount -DeploymentUrl "https://rdbroker.wvd.microsoft.com"
Get-RdsTenant
New-RdsRoleAssignment -RoleDefinitionName "RDS Owner" -SignInName "xxxx#xxxx.onmicrosoft.com" -TenantName "joywvd"
Then I run the Get-RdsTenant command again, and use fiddler to catch the request, get the token, decode in the https://jwt.io/, it appears like below.
The aud and scp should be the same as your token, you can also decode your token to check, then I use postman to call the https://rdweb.wvd.microsoft.com/api/feeddiscovery/webfeeddiscovery.aspx, it works.
Omg I just figured it out by comparing the token I got from the msft rdweb application:
From the RDWeb App:
"aud": "https://mrs-prod.ame.gbl/mrs-RDInfra-prod",
From my App:
"aud": "https://mrs-Prod.ame.gbl/mrs-RDInfra-prod",
....
Yes I was using an uppercase P in - mrs-Prod. And the msft app was using a lowercase p in mrs-prod.
I'm flabbergasted, angry and excited all at the same time.
For the record I copied my value directly from Azure in my apps api permissions screen.

AADSTS65001: The user or administrator has not consented to use the application with ID '<application ID>

We following the v2 of the OAuth2 of Microsoft Code grant flow as documented in the following,
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
After we created an application in App Register under Microsoft Azure, and try to get the code from the following url
https://login.microsoftonline.com/concept4.net/oauth2/v2.0/authorize?client_id=&response_type=code&redirect_uri=https://postman-echo.com/get&response_mode=query&scope=profile%20openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fuser.read&state=skip_get_token2&prompt=consent
Then we got the following error
{"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID '' named 'c4app2019'. Send an interactive authorization request for this user and resource.\r\nTrace ID: 46424a2f-a3a2-45da-8902-888f5ca61c00\r\nCorrelation ID: 49d0a6ad-e158-4bc9-97b8-a6391c6470bb\r\nTimestamp: 2019-12-11 07:51:31Z","error_codes":[65001],"timestamp":"2019-12-11 07:51:31Z","trace_id":"46424a2f-a3a2-45da-8902-888f5ca61c00","correlation_id":"49d0a6ad-e158-4bc9-97b8-a6391c6470bb","suberror":"consent_required"}
Any idea what permission we need to grant to our application?
I can not reproduce your issue on my side. Here are my steps for your reference.
1.Create an application with User.Read and profile permissions.
2.Since the permissions I added don't need admin consent, so I can consent by the first time I login.
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=59437d85-46f8-409c-8211-b3db91a8b0e5
&response_type=code
&redirect_uri=http://localhost
&response_mode=query
&scope=https://graph.microsoft.com/User.Read
&state=12345
3.Get the token by using the code I got from step2
To locate your issue, please provide the screenshot like step2(App registrations->your application->API permissions). And the value of scope you used to get code/token.
In case it's helpful to anyone, I was running into the same problem using the magical AzureServiceTokenProvider class from the Microsoft.Azure.Services.AppAuthentication.1.3.1 package. Very simple code
var tokenProvider = new AzureServiceTokenProvider();
string token = tokenProvider.GetAccessTokenAsync("https://mytenant.onmicrosoft.com/8a0bec0f-x-x-x-x").GetAwaiter().GetResult(); // Application ID URI
My error message was
AADSTS65001: The user or administrator has not consented to use the application with ID 'd7813711-9094-4ad3-a062-cac3ec74ebe8'. Send an interactive authorization request for this user and resource.
I couldn't find this d7813711 guid anywhere in my Azure AD. After looking into how this class works in a decompiler, it turns out when you don't specify an app ID, the class defaults to this guid. Maybe this guid is valid across tenants in Azure? To fix the issue so you can get a token for your app, simply add this as an authorized client application.
[Additional test 1]
Step 1:
I have create another app the use less API permission, which has the same issue
Step 2:
Get code by the following url
https://login.microsoftonline.com/concept4.net/oauth2/v2.0/authorize?client_id=15bf7752-....-c51cd145174c&response_type=code&redirect_uri=https://postman-echo.com/get&response_mode=query&scope=https://graph.microsoft.com/User.Read&state=skip_get_token2
and got
Step 3:
It seems working
It seems that the scope in Microsoft document for getting code and token is not correct or need some additional permission.
We also had this issue. We have updated our graph client to a newer version. We have done the following steps:
Revoke all admin consent
Remove all permissions
Add removed permissions back
Grant admin consent
I hope this will help someone with troubleshooting.
I had a similar problem, following this video step by step on how to set up your App Registration in AAD and test it with Postman solved my problem (I was missing some details in the configuration), I hope this help

Asking for user info anonymously Microsoft Graph

In an old application some people in my company were able to get info from Microsoft Graph without signing users in. I've tried to replicate this but I get unauthorized when trying to fetch users. I think the graph might have changed, or I'm doing something wrong in Azure when I register my app.
So in the Azure portal i have registered an application (web app), and granted it permissions to Azure ad and Microsoft graph to read all users full profiles.
Then I do a request
var client = new RestClient(string.Format("https://login.microsoftonline.com/{0}/oauth2/token", _tenant));
var request = new RestRequest();
request.Method = Method.POST;
request.AddParameter("tenant", _tenant);
request.AddParameter("client_id", _clientId);
request.AddParameter("client_secret", _secret);
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("resource", "https://graph.microsoft.com");
request.AddParameter("scope", "Directory.Read.All");
I added the last row (scope) while testing. I still got a token without this but the result is same with or without it.
After I get a token I save it and do this request:
var testClient = new RestClient(string.Format("https://graph.microsoft.com/v1.0/users/{0}", "test#test.onmicrosoft.com")); //I use a real user here in my code ofc.
testRequest = new RestRequest();
testRequest.Method = Method.GET;
testRequest.AddParameter("Authorization", _token.Token);
var testResponse = testClient.Execute(testRequest);
However now I get an error saying unauthorized, Bearer access token is empty.
The errors point me to signing users in and doing the request, however I do not want to sign a user in. As far as i know this was possible before. Have Microsoft changed it to not allow anonymous requests?
If so, is it possible to not redirecting the user to a consent-page? The users are already signed in via Owin. However users may have different access and i want this app to be able to access everything from the azure ad, regardless of wich user is logged in. How is the correct way of doing this nowadays?
Or am I just missing something obvious? The app has been given access to azure and microsoft graph and an admin has granted permissions for the app.
Edit: just to clarify, i tried both "Authorization", "bearer " + _token.Token, and just _token.Token as in the snippet.
Yes, it's still possible to make requests to Graph without a user present using application permissions. You will need to have the tenant admin consent and approve your application.
Edit / answer: Adding the 'Authorization' as a header instead of a parameter did the trick. It works both with 'bearer token' and just 'token'

Why Azure AD fails to login non-admins in multi-tenant scenario?

Environment:
Two Azure ADs: Company, Customers
Company publishes an ASP.NET5 web app called Portal, the app is setup to be multi-tenant.
Customers have 2 user: user (who is just a user) and admin (who is a Global Administrator in the directory).
Portal, is initially set up to ask for 1 Application Permission: Read Directory Data
-
Here comes the flow that I went through, and I believe Azure AD misbehaves at multiple steps. Please point out if I am missing something.
I open the web app, and first try to sign in as admin
I have to consent to the Read Directory data permission, so I do that
Application appears (I have no roles assigned yet, which is fine) -- so far everything works.
I re-open the web-app in a new incognito session, and try to sign in as the user
Now, I get [AADSTS90093: This operation can only be performed by an administrator. Sign out and sign in as an administrator or contact one of your organization's administrators.] -- the admin already consented, so why do I get this??
I go to Company AD and change the application permissions to include Read & Write Directory data
I go to Customer AD check the app Portal and the dashboard already shows the new permission listed. No one had to consent! The admin do not see any change even on next login. How is this not a security hole?
My understanding of https://msdn.microsoft.com/en-us/library/azure/dn132599.aspx is that Application Permissions are not deprecated.
UPDATE
My configuration in the WebApp:
app.UseOpenIdConnectAuthentication(options =>
{
options.ClientId = Configuration.Get("ActiveDirectory:ClientId");
options.Authority = String.Format(Configuration.Get("ActiveDirectory:AadInstance"), "common/"); //"AadInstance": "https://login.windows.net/{0}"
options.PostLogoutRedirectUri = Configuration.Get("ActiveDirectory:PostLogoutRedirectUri"); //"PostLogoutRedirectUri": "https://localhost:44300/"
options.TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
// The following commented-out line should work according to
// http://stackoverflow.com/questions/29317910/why-does-the-role-claim-have-incorrect-type
// But, it does not work in ASP.NET5 (currently), so see the "Hack." down below
// RoleClaimType = "roles",
ValidIssuers = new[] { "https://sts.windows.net/a1028d9b-bd77-4544-8127-d3d42b9baebb/", "https://sts.windows.net/47b68455-a2e6-4114-90d6-df89d8468abc/" }
};
options.Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
// This ensures that the address used for sign in and sign out is picked up dynamically from the request,
// which is neccessary if we want to deploy the app to different URLs (eg. localhost/immerciti-dev, immerciti.azurewebsites.net/www.immerciti.com)
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
context.ProtocolMessage.RedirectUri = appBaseUrl;
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
return Task.FromResult(0);
},
AuthorizationCodeReceived = async context =>
{
// Get Access Token for User's Directory
try
{
var identity = (ClaimsIdentity)context.AuthenticationTicket.Principal.Identity;
// Hack. TODO: keep an eye on developments around here
foreach (var claim in identity.FindAll("roles"))
{
// Readd each role with the proper claim type
identity.AddClaim(new Claim(identity.RoleClaimType, claim.Value, claim.ValueType, claim.Issuer, claim.OriginalIssuer));
}
}
catch (AdalException)
{
context.HandleResponse();
context.Response.Redirect("/Error/ShowError?errorMessage=Were having trouble signing you in&signIn=true");
}
}
};
};
Thanks for the information you've provided. I'm going to answer #7 first, because it looks pretty alarming. It does at first glance look like a security hole, but it's not. It's a bug in the Azure Management Portal that we are working to fix. In the "customers" tenant view, the UX is showing the permissions that the application (defined in the company tenant) is requesting. It should be showing the permissions actually granted in the "customers" tenant. In this case, if your app actually tries a call to write to the Graph API it'll get an access denied error. Anyways - not a security hole - but can sure understand why it looked that way to you - so sorry about this. We'll try to get this fixed as soon as we can.
On to some of your other questions about consent behavior... BTW this is something we are looking to improve in our documentation. Anyways, I'll try and answer this broadly in terms of the design behavior, because it looks like you've changed your app config multiple times.
If you pick any app permissions (not delegated permissions), the consent UX defaults to the "consent on behalf of the organization" experience. In this mode the consent page ALWAYS shows, whether the admin consented previously or not. You can also force this behavior if you make a request to the authorize endpoint with the QS parameter of prompt=admin_consent. So let's say you went down this path AND the only permission you have is app-only "Read Directory" and the admin consents. Now a user comes the user doesn't have any grant that allows them to sign in and get an id_token for the app (Read Directory app-only is not currently good for this), so the consent dialog tries to show the admin on behalf of org consent, but this is a non-admin so you get the error.
Now, if you add the delegated "sign me in and read my profile" permission for the app, and have your admin reconsent, you'll see that now the user will not be prompted for consent.
What I'll do is go back to our team and see whether ANY directory permission (app only or delegated) should allow any user to get a sign in token. One could argue that this should be the case.
HTHs,

Resources