I have an ASP.NET Core 3.1 web API application that is protected by Azure Authentication. I also have an Angular 11 application that calls the web API.
They work fine on our integration environment. But authentication fails on the test environment.
The Angular app authenticates fine using msal-angular library, it gets the access token and includes it when calling the web API. But the web API fails to retrieve the identity from the bearer token.
After deleting and recreating both (frontend and backend) Azure APP registrations several times and after reconfiguring them, the authentication still fails. I don't know what is the cause, so this is why I need to diagnose it.
I have enabled JwtBearerMiddlewareDiagnosticsEvents and I can see 'Begin OnAuthenticationFailedAsync' and 'End OnAuthenticationFailedAsync' on the logs. But it isn't enough information. It tells you no more that the events are raised, no additional information.
I've read the following article on documentation that tells you how to configure logging in MSAL.NET, but I don't think it's applicable to web API.
https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-logging-dotnet
So the question is: How can I figure out why the authentication is failing? Is there any way to log the cause?
UPDATE:
I'm closer, I set log level to trace and now I see the following on the log of asp.net core web api app:
IDX10511: Signature validation failed. Keys tried: '[PII is hidden.
For more details, see https://aka.ms/IdentityModel/PII.]'. \nkid:
'[PII is hidden. For more details, see
https://aka.ms/IdentityModel/PII.]
UPDATE:
I get the following error:
Bearer was not authenticated. Failure message: IDX10214: Audience
validation failed. Audiences: '242e8f40-d795-43bc-8ac8-8eed3df71745'.
Did not match: validationParameters.ValidAudience:
'b99e89fc-8a1c-4605-8c18-796d7064037e' or
validationParameters.ValidAudiences: 'null'.
'242e8f40-d795-43bc-8ac8-8eed3df71745' is the AppId on the web api app registration manifest.
'b99e89fc-8a1c-4605-8c18-796d7064037e' is the client id of the web api registration.
The scope is 'api://242e8f40-d795-43bc-8ac8-8eed3df71745/api-access'.
This is how I set the scope:
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set('https://api.example.com/*', ['api://242e8f40-d795-43bc-8ac8-8eed3df71745/api-access']);
return {
interactionType: InteractionType.Popup,
protectedResourceMap
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return {
interactionType: InteractionType.Popup,
authRequest: {
scopes: ['api://242e8f40-d795-43bc-8ac8-8eed3df71745/api-access']
},
loginFailedRoute: '/login-failed'
};
}
Your problem has been solved. The problem is that the configuration of the web api is wrong. You need to create them again and reconfigure .
According to my experience, you need to create two applications in Azure, one representing the web api application and the other representing the client application (ie Angular 11 application).
Then you need to expose the api of the web api application in Azure and add the Angular 11 application as a client application to the web api application.
Finally, when requesting an access token, you need to set the scope to: api://{web api client id}/{scope name}.
Related
I am trying to have silent authentication with Azure AD from a desktop app. I am using MSAL.NET 4.72.2, specifically this particular call. It is not recommended by Microsoft for some specific reasons, none of them applies to my case. However, it is supposed to work silently, like in a traditional case, you pop up a login WinForm in the app and provide user name and password. Then click OK and eventually it comes down to this call:
authResult = await app.AcquireTokenByUsernamePassword(
scopes,
ClientApp.SafeStorage.DecryptString(ClientApp.Username),
ClientApp.SafeStorage.DecryptString(ClientApp.Pwd))
.ExecuteAsync(CancellationToken.None)
.ConfigureAwait(true);
where ClientApp.Username and ClientApp.Pwd are username and password.
The problem is that this call fails against Azure AD without MFA enabled for that account:
Microsoft.Identity.Client.MsalServiceException: 'A configuration issue is preventing authentication - check the error message from the server for details. You can modify the configuration in the application registration portal. See https://aka.ms/msal-net-invalid-client for details. Original exception: AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.
Trace ID: 50d49e42-d5e4-4cd8-984d-540c3d538a00
Correlation ID: 7874e878-1084-4f32-9679-69030a2dbed8
Timestamp: 2022-11-02 23:09:46Z'
You need to enable Public Client Flows on your app registration. As below:
When I am trying to run user flow manually from portal.azure.com by clicking 'Run user flow' at the bottom I can select the application and reply URL.
I have defined two reply URLs:
https://localhost:5001/signin-oidc
https://jwt.ms
I select the first reply URL and below that information the 'Run user flow endpoint' is generated automatically. When I copy this endpoint and try to call from my blazor app it is working correctly. I can log in and change my profile info. Then, I can click continue to reply to my blazor app.
After clicking I want to get back to my app but I have an error:
Error.
An error occurred while processing your request.
Request ID:
00-f13479803a4cdeb7d3e203f0910e3688-a0619e9d48caf391-00
Details OpenIdConnectAuthenticationHandler: message.State is null or
empty.
When I set the reply URL to jwt.ms everything works fine.
How to call Edit Profile User Flow from the blazor app properly?
There are below few workaround may help to achieve the above requirement
Kindly check the signuporsignin.xml b2c userflow policy for correct tenant id and public policy uri in that policy file specifically check the technical profile id and inherent output claims regarding post successful authentication with Azure ad b2c .
Since after successful authentication to the blazor app you are able to login and able to change the profile info in the signinsignup user flow page successfully ,But when returning to the blazor client application you are encountering an error that displays the State parameter is null or empty, which inherently implies that the token access authentication is not passed on or the token has is broken/missing or misinterpreted during the changes of pages.
Thus i would suggest you to please check the program.cs file in the blazor client application code whether it includes the below code snippets or not.
builder.Services.AddMsalAuthentication(options => { builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); options.ProviderOptions.DefaultAccessTokenScopes.Add("api://https://domain.onmicrosoft.com/cxxxx-1xx-4xxxe-aad0-4xxxxx/API.Access"); });
The above code snippet states that the blazor client application has exposed his api to the blazor server application as a provider.
Further also please check the authentication.razor file for the parameter to be passed on public string
Once the above has been checked and found correct then please ensure that the appsettings.json file on both the client & server applications has the correct respective client Id and the policy name mentioned. Also check the Azure ADb2c app registration for the details in blazor web assembly application.
For more information please refer the below links:-
Secure an ASP.NET Core Blazor WebAssembly hosted app with Azure Active Directory | MICROSOFT DOCUMENTATION
I have set up a Web Application with ASP.NET Razor Pages with -> Individual User Accounts -> Connect to an existing user store in the cloud (Azure AD B2C).
This works really well and I could both sign up and sign in to the web application.
However when I follow the guide for API I don't understand how to sign in.
The example Controller /weatherforecast simply returns a HTTP 401 when the web application is started.
Looking at the file structure I can't find any clues either but this could be similar to scaffolding I guess.
https://stackoverflow.com/a/50677133/3850405
If I comment out [Authorize] from WeatherForecastController I get a HTTP 200 so what I need is probably just a token from Azure AD B2C that is being sent to the Controller in the GET request.
I know that the B2C tenant and application work since I use the same application for the API as I did with the Web Application. It was set up using Microsofts own guide:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant
Update 2020-07-27:
Applications are now Legacy and App registrations should be used instead. See this guide:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications?tabs=app-reg-ga#register-a-web-application
Old:
Fixed it using these guides:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/tokens-overview
https://learn.microsoft.com/en-us/azure/active-directory-b2c/access-tokens
I had some trouble where I got the error "AADB2C90205: This application does not have sufficient permissions against this web resource to perform the operation. numerous times. Turned out I had not declared the correct scopes for the application.
First step is therefore to make sure you have a read scope for your Azure AD B2C application under Published scopes:
Then under API access add your application with the scope read.
Then perform a GET request with this format, simplest way to test is to use it in Chrome or any other browser:
https://<tenant-name>.b2clogin.com/tfp/<tenant-name>.onmicrosoft.com/<policy-name>/oauth2/v2.0/authorize?
client_id=<application-ID>
&nonce=anyRandomValue
&redirect_uri=https://jwt.ms
&scope=https://<tenant-name>.onmicrosoft.com/api/read
&response_type=code
Make sure the redirect_uri is present as Reply URL for your application.
This should give you a result like after logging in like https://jwt.ms/?code=... or https//localhost:44376/signin-oidc?code= depending on redirect_uri. Microsoft example uses https://jwt.ms but I prefer to keep my codes on domains that I control.
Copy the value from code parameter and then perform a POST request, I use Postman.
POST <tenant-name>.onmicrosoft.com/oauth2/v2.0/token?p=<policy-name> HTTP/1.1
Host: <tenant-name>.b2clogin.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=<application-ID>
&scope=https://<tenant-name>.onmicrosoft.com/api/read
&code=eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMC...
&redirect_uri=https://jwt.ms
&client_secret=<app-key>
client_secret is from Keys:
Correct response should look like this:
Then you can copy the value for access_token and access your local API with Bearer Authorization. To see the content of your access_token you can copy the value to https://jwt.ms/
I have a project that hosts the IdentityServer4 and I am attempting to also host in the same project a Web API, which accepts the access-token.
My question is, is possible that a single project contains the IdentityServer and an Web API that consume the same IdentityServer?
EDIT: The API must be secured with the Authorize attribute
I have an identity server 4 project, in the same project there is an API for CIUD of the clients. (Lets call it developer console api).
I then have a side project with is an asp .net core project that contains the actual razor pages for the Developer console it access the API within the Identity server project.
The reason i did it this way is that only one project should be updateing the database. So to update the database owned by the identity server it was decided the the API for accessing it should also be within the same project.
Yes you can have a web api from within your Identity server 4 project.
Configure service
services.AddAuthentication(IdentityServerConstants.DefaultCookieAuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = settingsSetup.Settings.Authority;
// name of the API resource
options.ApiName = "testapi";
options.RequireHttpsMetadata = false;
});
Configure
I think it needs to have both of these.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseAuthentication();
app.UseIdentityServer();
Endpoints
Because the requests are sent using the access token as a bearer token then the authorize for each of the API calls needs to include the authencationScheme. I havent exactly figured out why but without this it doesnt work.
[HttpGet("Client/List")]
[Authorize(AuthenticationSchemes = "Bearer")]
public ActionResult ClientList()
{
}
While #DaImTo's answer is correct and working and it's developed by IdentityServer team, it uses Introspection Endpoint which means for every request AddIdentityServerAuthentication will create a http request and send it to your server, which is the same app.
I developed a library called IdentityServer4.Contrib.LocalAccessTokenValidation which do the exact same thing but without using Introspection Endpoint. It will authenticate the token directly from TokenStore which is configured in Services. You can use it if you are interested.
nuget link : https://www.nuget.org/packages/IdentityServer4.Contrib.LocalAccessTokenValidation
github link : https://github.com/Kahbazi/IdentityServer4.Contrib.LocalAccessTokenValidation
I'm having hard times trying to use Azure AD B2C to authenticate My Web API.
I'll start with some background
I created mobile application which is using Azure AD B2C to authenticate users. I'm creating a WebView which display this url:
User is asked to login to azure ad, if the login data is successfull i'm receiving a response containing the access token - this part went smooth, everything works properly.
Now i want to create backend Web Api. I created ASP NET Core Web application which allows me to choose authentication method. I choose the Azure AD authentication so the template generated all required data for me. The relevant part in the code is here:
I updated all required config properties to match my azure settings. At this point i would expect to be able to call the API using access token i received on the mobile app. I run mobile app locally, signed in, received access token, copied it and tried to call my web api(hosted in IIS express) using postman ( with authorization header "Bearer ..." ). Unfortunately with no luck - i'm receiving 401 with following header:
Bearer error="invalid_token", error_description="The signature key was
not found"
I thought token is enough to Authorize the API - i understand this is a whole point of OAuth. Am i missing something ? Should i have some additional config ? I Noticed the config is missing the sign in policy ( which seems to be required by AD B2C name so i tried adding that:
var validationParameters = new TokenValidationParameters
{
AuthenticationType = "MY_POLICY",
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"],
Audience = Configuration["Authentication:AzureAd:Audience"],
TokenValidationParameters = validationParameters
});
But this didn't work too. Will appreciate any help.
EDIT
I found following error in Visual Studio logs:
Bearer was not authenticated. Failure message: IDX10501: Signature
validation failed. Unable to match 'kid': '...'
#juunas comment help me to find the issue. I inspected outgoing requests with fiddler and i found that with this piece of code:
Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"]
The request was being send to following address:
https://login.microsoftonline.com/MYTENANTID/.well-known/openid-configuration
There are two issues with above:
It's not using v2 endpoint. Proper link for B2C should always use v2 so it would look like:
https://login.microsoftonline.com/MYTENANTID/v2.0/.well-known/openid-configuration
It was not adding sign in policy to the link ( even if i set it in token options )
I managed to make it work with removing "Authority" parameter and changing the configure auth function to following:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
MetadataAddress = string.Format("https://login.microsoftonline.com/{0}/v2.0/.well-known/openid-configuration?p={1}",
Configuration["Authentication:AzureAd:TenantId"], "MYPOLICY"),
AuthenticationScheme = "MYPOLICY",
Audience = Configuration["Authentication:AzureAD:ClientId"],
});