Microsoft Identity Platform and ValidAudiences - c#

I am working on updating our API (core 3.1) auth to use the latest Microsoft Identity nuget for use with MSAL for an Angular UI application. We have Azure Functions that will call into our API's using a Managed Service Identity and have setup several new app registrations for each API to use with MSAL in Angular. The same API's we call from an Azure function will also be called from the Angular UI. The problem I am running into is that I need to accept up to four different audiences in order not to break auth for everything.
Audiences needed:
client id of the API
https://management.azure.com/ for MSI
https://management.core.windows.net/ for MSI
client id of another app registration we use to generate tokens for automation testing
I am attempting to set the audiences in a list as follows:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options => { }, options =>
{
options.Authority = Configuration["Authentication:Jwt:Authority"];
options.Instance = Configuration.GetSection("AzureAd")["Instance"];
options.ClientId = Configuration.GetSection("AzureAd")["ClientId"];
options.Domain = Configuration.GetSection("AzureAd")["Domain"];
options.TenantId = Configuration.GetSection("AzureAd")["TenantId"];
options.TokenValidationParameters.ValidateAudience = true;
var audience = new List<string>();
audience.Add(Configuration["AzureAd:ClientId"]);
audience.AddRange(new string[] {"https://management.azure.com/",
"https://management.core.windows.net/", "other api client id"});
options.TokenValidationParameters.ValidAudiences = audience;
});
When I attempt to call an endpoint via Swagger using a token created by another app (#4), I get this error:
IDX10214: Audience validation failed. Audiences: 'System.String'. Did not match: validationParameters.ValidAudience: 'System.String' or validationParameters.ValidAudiences: 'System.String'.
I also noticed when looking at the context in the events that none of the audience values I setup at runtime are present when the events trigger. ValidAudience and ValidAudiences are both null.
I need to figure out how to persist these settings in the events as my guess is that is why the audience validation is failing.

Related

Azure B2C Authentication (angular + .net core Web API) - Neither scope or roles claim was found in the bearer token

i try to do an angular application with MSAL-angular and Azure B2C for authentication.
I am able to authenticate the angular app with Azure B2C (i have create a susi flow) and obtain the token as show in image below
so i have create a .net core web api project and modify the appsetting configuration and the start up with this code :
appsetting.json :
"AzureAdB2C": {
"Instance": "https://{mytenat}.b2clogin.com/tfp",
"ClientId": "8xxxx-xxxx-xxxx-xxxx-xxxxxxxxc",
"Domain": "{mytenat}.onmicrosoft.com",
"SignUpSignInPolicyId": "B2C_1_susi"
}
startup.cs
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
Configuration.Bind("AzureAdB2C", options);
options.TokenValidationParameters.RoleClaimType = "roles";
options.TokenValidationParameters.NameClaimType = "name";
},
options => { Configuration.Bind("AzureAdB2C", options); });
// By default, the claims mapping will map claim names in the old format to accommodate older SAML applications.
//'http://schemas.microsodt.com/ws/2008/06/identity/clains/role' instead of 'roles'
// This flag ensures that the ClaimsIdentity claims collection will be build from the claims in the token
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
//services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
//{
// // The claim in the Jwt token where App roles are available.
// options.TokenValidationParameters.RoleClaimType = "roles";
//});
but if i try to run localy the project and call it with postman i have this error :
**System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token. **
i don't understand where is the error.
can you help me?
thanks
In your question, you have created a web api application and angular app in Azure AD B2C. Next, you need to expose the api of the web api application, and then add permissions to the angular app.
First go to web api.
Then go to angular app>API permissions> add a permission>My APIs>your web api app.
Finally, when you parse the token, you will see the scp:access claim.

Azure AD Authentication in ASP.NET Core 2.2

Im currently struggling to connect a ASP.NET Core 2.2 Web API to an existing Azure AD. I based my configuration upon this sample code by the ASP.NET Core team. Cookies were replaced with JWTs.
Unable to retrieve document from metadata adress
Now I face the following error message:
IOException: IDX10804: Unable to retrieve document from: {MetadataAdress}.
- Microsoft.IdentityModel.Protocols.HttpDocumentRetriever+<GetDocumentAsync>d__8.MoveNext()
- System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
- System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
- Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever
+<GetAsync>d__3.MoveNext()
- System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
- System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
- Microsoft.IdentityModel.Protocols.ConfigurationManager+<GetConfigurationAsync>d__24.MoveNext()
When I call the URL directly, I receive an instant response with the configuration file. However, the code does not seem to be able to do it. Im not sure what the reason could be.
Azure AD Configuration Syntax
The most likely cause of this issue is a configuration mistake. Maybe I have mistaken a field's syntax or am missing an important value.
Connection Info Fields
The connection info fields are provided like this:
TenantId: {Tenant-GUID}
Authority: https://login.microsoftonline.com/{TenantId}
Resource: https://{resource-endpoint}.{resource-domain}
ClientId: {Client-GUID}
ClientSecret: {ClientSecret}
Service Configuration
The authentication service configuration in the Startup.cs looks like this:
services
.AddAuthentication(options => {
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddJwtBearer()
.AddOpenIdConnect(options => {
options.ClientId = this.ClientId;
options.ClientSecret = this.ClientSecret;
options.Authority = this.Authority;
options.Resource = this.Resource;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.SignedOutRedirectUri = "/signed-out";
options.Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = async context =>
{
var request = context.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(
request.Scheme, request.Host, request.PathBase, request.Path
);
var credentials = new ClientCredential(this.ClientId, this.ClientSecret);
var authContext = new AuthenticationContext(
this.Authority,
AuthPropertiesTokenCache.ForCodeRedemption(context.Properties)
);
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.ProtocolMessage.Code,
new System.Uri(currentUri),
credentials,
this.Resource
);
context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
// Custom
options.MetadataAddress = $"{this.Authority}/federationmetadata/2007-06/federationmetadata.xml";
options.RequireHttpsMetadata = false; // Dev env only
}
Existing APIs
There is a bunch of existing Web APIs that connect to this Azure AD. Sadly, they are all using the full .NET Framework. They use the UseWindowsAzureActiveDirectoryBearerAuthentication method from the Microsoft.Owin.Security.ActiveDirectory namespace's WindowsAzureActiveDirectoryBearerAuthenticationExtensions.
Another thing they use is the HostAuthenticationFilter with an authentication type of Bearer.
Questions
What is the problem?
How can I resolve this issue?
How can I use these components together?
ASP.NET Core 2.2
JWT Bearer Authentication
Azure AD (token validation + claim extraction only - creation is handled by other service)
You are using OpenIDConnect libraries and point them to WS-Federation metadata (/federationmetadata/2007-06/federationmetadata.xml). This is not going to work.
The correct metadata endpoint for OpenIDConnect is /.well-known/openid-configuration. This is described here. Change that first, and then return cookies.
UPDATE
What I oversaw, was that you are protecting WebAPI. You say the middleware to use JwtBearer as default authentication cheme, but you also include a challenge scheme to be OIDC. That doesn't really make sense for me. Why do you want an OIDC challenge scheme for an WebAPI?
Here you can find the ASP.NET Core samples about JwtBearer. Here the Azure AD samples demoing WebApp calling WebApi (also bearer for the WebAPI, OIDC for the App FrontEnd.
There are no samples for JWT Bearer Auth using OIDC challenge. Why do you want to implement that? What is the case? You might be looking at implementing multiple Authentication schemes, which is possible. But not having one scheme for Authentication and another for challenge...
If by updating/removing the wrong metata changes the error message, include that in your original question. As it is now - the pure error message is that OIDC Middleware cannot parse WS-Federation metadata. Which is expected.
Source of the problem
After some testing I managed to identify the problem: Apparently the main cause of this issue was network related. When I switched from our company's to an unrestricted network the authentication was a success.
The fix
I had to configure a proxy and provide it to the JwtBearer and OpenIdConnect middleware. This looks like this:
var proxy = new HttpClientHandler
{
Proxy = new WebProxy("{ProxyUrl}:{ProxyPort}") { UseDefaultCredentials = true; },
UseDefaultCredentials = true
};
services
.AddJwtBearer(options => {
// ... other configuration steps ...
options.BackchannelHttpHandler = proxy;
})
.AddOpenIdConnect(options => {
// ... other configuration steps ...
options.BackchannelHttpHandler = proxy;
})
Metadata adress
#astaykov was right that the metadata adress is indeed incorrect. I had this feeling as well but kept it as previous APIs were running successfully with it. During problem testing I removed it, too, but it would not make a difference due to the network issues.
After the network issues were resolved, using the default metadata adress worked. The custom one failed - as expected when using a different authentication schema.

Creating resource groups from Azure Web App Managed Service Identity

I'm trying to build a Bot using MS Bot framework and this bot is hosted as an Azure Web App. I've added code to create resource groups using the Microsoft.Azure.Management.Fluent APIs
AzureCredentialsFactory f = new AzureCredentialsFactory();
var msi = new MSILoginInformation(MSIResourceType.AppService);
var msiCred = f.FromMSI(msi, AzureEnvironment.AzureGlobalCloud);
var azureAuth = Azure.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.BodyAndHeaders)
.Authenticate(msiCred);
var azure = azureAuth.WithSubscription(subscription);
var resourceGroup = azure.ResourceGroups.Define(rg)
.WithRegion(Region.EuropeWest)
.Create();
This code is levering the Managed Service Identity of the Web app. I've made this web app "Owner" of the Azure subscription.
when i execute this code i keep getting this exception
Exception: The access token has been obtained from wrong audience or resource ’https://management.core.windows.net'. It should exactly match (including forward slash) with one of the allowed audiences ‘https://management.core.windows.net/’,’https://management.azure.com/’.
I never set the audience or resource by hand and don't see any option on how to do this.
When i change my code to use a service principal i created myself it works great
ServicePrincipalLoginInformation loginInfo = new ServicePrincipalLoginInformation()
{
ClientId = _clientId,
ClientSecret = _clientSecret
};
var credentials = new AzureCredentials(loginInfo, _tenantId, AzureEnvironment.AzureGlobalCloud);
var azureAuth = Azure.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.BodyAndHeaders)
.Authenticate(credentials);
How to set this audience or resource or what am i doing wrong?
How to set this audience or resource or what am i doing wrong?
I also can reproduce this issue on my side. It seems it is the SDK issue. You could report it to the Azure SDK github issue.
Update:
According to issue-4090, it has been fixed in version 1.7, you could test again if it is released.

How can I set audience with OpenIdConnectMiddleware?

I am using the OpenIdConnectMiddleware in an ASP.NET Core 2.0 app, with Auth0 as authentication.
I have followed this guide to implement authentication via Auth0, and can successfully log in.
Now I want to use the retrieved access token to access a separate API; for this to work I need to include an audience parameter when authorizing with Auth0, as described here.
Since the OpenID Connect middleware handles the authorization with Auth0, and there is no Audience setting on the OpenIdConnectOptions, how can I specify the audience parameter that should be passed to the /authorize endpoint?
Found the answer in this blog post from Jerrie Pelser. I need to use the OnRedirectToIdentityProvider event handler to set audience:
options.Events = new OpenIdConnectEvents {
OnRedirectToIdentityProvider = context => {
context.ProtocolMessage.SetParameter("audience", "https://my/api");
return Task.CompletedTask;
},
...
}

Azure AD 401 error - Authenticating from angular client

I have a separate angular client that I want to authenticate to using Azure AD. I am using ADAL JS and all of that seems to be working fine. I get redirected to the AD login page, and then sent back to my application. I can see the token getting passed with each subsequent http request.
However, when I try to make a secured request to my Web API I receive a 401 Unauthorized error. I am loosely following the guide here for setup. I say loosely because I'm not using MVC, my client is in a separate codebase entirely.
I am positive that my user has access to this application.
My Auth Configuration stuff looks like:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(new WindowsAzureActiveDirectoryBearerAuthenticationOptions()
{
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigurationManager.AppSettings["AzureADAudience"],
},
Tenant = ConfigurationManager.AppSettings["AzureADTenant"],
AuthenticationType = "OAuth2Bearer"
});
Audience: https://login.windows.net/xyz.onmicrosoft.com/myappname
Tenant: xyz.onmicrosoft.com
The controller I'm locking down is decorated like this:
[HostAuthentication("OAuth2Bearer")]
[Authorize]
[RoutePrefix("Auth")]
Is your SPA hosted with your backend? If so, then you need to change your audience to the Client ID.
ValidAudience = ConfigurationManager.AppSettings["ida:ClientID"]

Categories