Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter Error Logs in AzureAD Auth Implementation - azure

We have integrated AzureAD for our API Authorization and API Auth is working fine but seeing error logs for Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter with below messages,
IDX40003: Neither tid nor tenantId claim is present in the token obtained from Microsoft identity platform.
IDX10516: Signature validation failed. Unable to match key: kid: '*'. Number of keys in TokenValidationParameters: '16'. Number of keys in Configuration: '0'. Exceptions caught: '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. token: '[PII of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Valid Lifetime: 'False'. Valid Issuer: 'False'**
Our APIs are being consumed by UI APP as well as via Swagger/Postman.
What should be root cause to it?

The error IDX10516: Signature validation failed. Unable to match key: kid occurs when the Kid in your decoded token which validates token signature is not valid.
So one needs to load keys from the openid provider.
code:
var openidConfiguration = new ConfigurationManager<OpenIdConnectConfiguration>(
$"https://login.microsoftonline.com/{tenantid}/v2.0/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever());
var config = await openidConfiguration.GetConfigurationAsync();
var parameteres = new TokenValidationParameters()
{
RequireAudience = true,
RequireExpirationTime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudiences = new[] { "<my client id or appId uri or resource identitifer>" },
ValidIssuers = new[] { $"https://sts.windows.net/{tenantid}/" },
IssuerSigningKeys = config.SigningKeys
};
AppId Uri , you can check from the portal : api://
Add the following to check for more clear descriptive exception in ConfigureServices method in Startup.cs :IdentityModelEventSource.ShowPII = true
Example:
public void ConfigureServices(IServiceCollection services)
{
...
IdentityModelEventSource.ShowPII = true;
...
}
Reference : Azure AD - Why can't I validate JWT token issued by Azure AD for my Web API? Getting "IDX10516: Signature validation failed" error - Stack Overflow

Related

Azure AD B2C API Access through Postman and Web app

As a follow-up on my question about how to setup a ROPC Flow. I want to access my API through the ROPC flow (currently using default user flows) and also through my web app which uses a custom policy on sign-in. This results in two different access tokens. On the left is access token received using the AcquireTokenSilent call and on the right is the access token received through postman with ROPC.
The custom policy token (on the left) gives an "Authorization has been denied for this request." error, while the token on the right is fine. I am assuming that the custom policy token does not work because it does not contain the tfp claim (and if it did, it would be a different one).
How can I set it up so that I can still use the ROPC flow while also using the custom policy? I would like to keep the current userjourney in the custom policy the same. Although if it is possible to somehow add ROPC as an option to it, then it would be fine.
Based on the description above, you are using two policy types - a user flow and a custom policy. And, you are attempting to get SSO between the two.
This is not a supported scenario. This is because the token uses different keys that signs the token.
If custom policies are required for your scenario, I suggest converting the user flow ROPC to a custom policy using this document https://learn.microsoft.com/en-us/azure/active-directory-b2c/add-ropc-policy?tabs=app-reg-ga&pivots=b2c-custom-policy
So I finally found a way to do this in .NET Framework, if you want a solution for .NET Core you would sadly have to look somewhere else.
In your startup add the following.
/*
* Configure the authorization OWIN middleware
*/
private void ConfigureAuthenticationAzure(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(CreateOptions(ClientId, SignUpSignInPolicy, azureDiscoveryEndpoint));
app.UseOAuthBearerAuthentication(CreateOptions(ClientId, ApiPolicy, azureDiscoveryEndpointAPI));
}
private OAuthBearerAuthenticationOptions CreateOptions(string audience, string policy, string discoveryEndpoint)
{
var metadataEndpoint = String.Format(discoveryEndpoint, Tenant, policy);
// This is the default check, in OnValidateIdentity, we check for more.
TokenValidationParameters tvps = new TokenValidationParameters
{
// This is where you specify that your API only accepts tokens from its own clients
ValidAudience = ClientId,
ValidateAudience = true,
AuthenticationType = policy,
NameClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier",
ValidateIssuer = true,
};
return new OAuthBearerAuthenticationOptions
{
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(metadataEndpoint)),
Provider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = async context =>
{
try
{
var authorizationHeader = context.Request.Headers.Get("Authorization");
var userJwtToken = authorizationHeader.Substring("Bearer ".Length).Trim();
var ticket = context.Ticket;
//var identity = ticket.Identity;
var jwtSecurityToken = new JwtSecurityToken(userJwtToken);
var expiration = jwtSecurityToken.ValidTo.ToLocalTime();
if (expiration < DateTime.Now)
{
log.Warn("The JWT token has expired.");
context.Rejected();
return;
}
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(discoveryEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdconfig = configManager.GetConfigurationAsync().Result;
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openIdconfig.SigningKeys,
ValidateIssuer = true,
ValidIssuer = $"{AzureIssuer.ToLower()}/v2.0/",
ValidateAudience = true,
ValidAudience = audience,
ValidateLifetime = true,
//ClockSkew = TimeSpan.Zero
};
var handler = new JwtSecurityTokenHandler();
SecurityToken securityToken;
var principal = handler.ValidateToken(userJwtToken, validationParameters, out securityToken);
var policyName = principal.FindFirst("tfp")?.Value;
// Add the name claim type for this authentication type
if (policyName.ToLower() == DefaultPolicy.ToLower()) // Sign In Only policy...
{
// Run specific code here for the policy that just sent a token back to the application...
context.Validated(ticket);
return;
}
else if (policyName.ToLower() == SignUpSignInPolicy.ToLower())
{
context.Validated(ticket);
return;
}
context.Rejected();
return;
}
catch(Exception ex)
{
context.Rejected();
return;
}
}
}
};
}

IDX10501: Signature validation failed. Unable to match key: kid

We want to switch our web apps to using Azure AD for authentication.
I'm using the example at: https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect
It wouldn't work initially but after some modifications I got authentication to work. But when it redirects back to my app I get this error: IDX10501: Signature validation failed. Unable to match key: kid
I'm using https://login.microsoftonline.com/{0}/v2.0 for the authority where {0} is my tenant id.
This is the authentication code
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
// Custom middleware initialization. This is activated when the code obtained from a code_grant is present in the querystring (&code=<code>).
app.UseOAuth2CodeRedeemer(
new OAuth2CodeRedeemerOptions
{
ClientId = AuthenticationConfig.ClientId,
ClientSecret = AuthenticationConfig.ClientSecret,
RedirectUri = AuthenticationConfig.RedirectUri
});
IdentityModelEventSource.ShowPII = true;
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// This is needed for PKCE and resposne type must be set to 'code'
UsePkce = true,
ResponseType = OpenIdConnectResponseType.Code,
// The `Authority` represents the v2.0 endpoint - https://login.microsoftonline.com/{0}/v2.0
Authority = AuthenticationConfig.Authority,
ClientId = AuthenticationConfig.ClientId,
ClientSecret = AuthenticationConfig.ClientSecret,
RedirectUri = AuthenticationConfig.RedirectUri,
PostLogoutRedirectUri = AuthenticationConfig.RedirectUri,
Scope = AuthenticationConfig.BasicSignInScopes + " Mail.Read User.Read", // a basic set of permissions for user sign in & profile access "openid profile offline_access"
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
},
// Handling SameSite cookie according to https://learn.microsoft.com/en-us/aspnet/samesite/owin-samesite
CookieManager = new SameSiteCookieManager(
new SystemWebCookieManager())
});
}
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> arg)
{
arg.ProtocolMessage.SetParameter("myNewParameter", "its Value");
return Task.CompletedTask;
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
context.TokenEndpointRequest.Parameters.TryGetValue("code_verifier", out var codeVerifier);
// Upon successful sign in, get the access token & cache it using MSAL
IConfidentialClientApplication clientApp = MsalAppBuilder.BuildConfidentialClientApplication();
AuthenticationResult result = await clientApp.AcquireTokenByAuthorizationCode(new[] { "Mail.Read User.Read" }, context.Code)
.WithSpaAuthorizationCode() //Request an authcode for the front end
.WithPkceCodeVerifier(codeVerifier) // Code verifier for PKCE
.ExecuteAsync();
HttpContext.Current.Session.Add("Spa_Auth_Code", result.SpaAuthCode);
// This continues the authentication flow using the access token and id token retrieved by the clientApp object after
// redeeming an access token using the access code.
//
// This is needed to ensure the middleware does not try and redeem the received access code a second time.
context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
//notification.HandleResponse();
//notification.Response.Redirect("/Error?message=" + notification.Exception.Message);
return Task.FromResult(0);
}
How do I get it to validate the signature?
Thanks,
Skye
Please check if below points help:
On successful authentication, Azure AD issues a signed JWT token (id token or access token). The resource application needs to know the public key of the certificate used sign the token in order to validate the token signature . An OWIN asp.net application can throw the following error IDX10501: Signature validation failed. Unable to match ‘kid’ or IDX10501: Signature validation failed. Unable to match key when it’s not able to find the kid to validate the token signature:
Please check and add valid audience and issuer. And check if the kid
its validating is for symmetric key.
Check if you are passing the correct metadata endpoint.
ex:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
MetadataAddress = "https://login.microsoftonline.com//.well-known/openid-configuration?appid="
PostLogoutRedirectUri = redirectUri,
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{validissuers.Last()}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
var openidconfig = configManager.GetConfigurationAsync().Result;
TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = true,
ValidAudience = config.Resource,
ValidateIssuer = true,
ValidIssuers = new[] { config.GetAuthority() },
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openidconfig.SigningKeys,
//or IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuperSecretPassword2"))
}
}
Azure AD by default uses a certificate to sign an OAuth2 JWT token
using an asymmetric algorithm (RS256). Alternatively a JWT token can
be signed with a “shared” secret using a symmetric algorithm (HS256).
References:
get a symmetric key signing token (aaddevsup.xyz)
c# - IDX10501- Stack Overflow

Azure AD Identity authentication error : IDX10214: Audience validation failed

I have an .net core 5 api that I want to protect using Azure AD, and then call it from a console app. So my case is only service-to-service authentication. I registered both the apps to Azure AD an then gave the permission to the console app on the api using App roles.
I succeeded to retrieve a token for the console app passing the right scope :
static async Task Main(string[] args)
{
var app = ConfidentialClientApplicationBuilder
.Create(<MY_CONSOLE_APP_ID>)
.WithClientSecret(<MY_CONSOLE_APP_CLIENT_SECRET>)
.WithAuthority(new Uri($"https://login.microsoftonline.com/<MY_TENANT_ID>"))
.Build();
var result = await app.AcquireTokenForClient(new String[]
{
"api://<MY_API_APP_ID>/.default"
})
.ExecuteAsync();
}
Then in the Startup class of the API, I added the following code :
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration, "AzureAd");
and in the appsettings.json I defined the required settings:
"AzureAd": {
"ClientId": "<MY_API_APP_ID>",
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<MY_TENANT_ID>"
}
Then I marked a controller as [Authorize] but when I call one of its endpoint I get the following error :
{
"code": 401,
"message": "IDX10214: Audience validation failed. Audiences: '[PII is hidden]'. Did not match: validationParameters.ValidAudience: '[PII is hidden]' or validationParameters.ValidAudiences: '[PII is hidden]'."
}
I also tried to prefix the clientId by api:// but got the same error. I decoded the token to ensure the audience was correct and it seems ok since I have "aud": "api://<MY_API_APP_ID>"
Do you have an idea of what I am missing ?
Whatever code provided looks good.
Make sure the Audience config matches the "aud" claim in the access token.
As that part of code is not provided here,
you may be missing an entry of audience in code configuration under services.AddAuthentication(…
Please check if you have given audience entry in any of these ways .
1
.AddJwtBearer(options =>
{
options.Authority = "";
//options.Audience = Configuration["Audience"];
};
(or)
2
.AddJwtBearer(options =>
{
options.Audience = clientId;
options.Authority = authority;
})
(or)
3)
.AddJwtBearer(options =>
{
options.Authority = "";
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
//here you give required valid audiences
ValidateIssuer = true,
ValidAudiences = new List<string>
{
"AUDIENCE1",
"AUDIENCE2"
}
or valid audiences can be like below in place of AUDIENCE1 or 2.
ValidAudiences = new List<string>
{
Configuration["Authentication:ClientId"]
}
The aud (audience) should match your API's id, and make sure required scopes are present .If these are fine , then check if the token you are trying to validate is ID token or Access token .It differs for API and graph api.

Azure AD B2C - CORS Policy Issue for Multi App Services

I've 2 projects: Project1 is main app (localhost:4016) Project2 is child app (localhost:4055) and sharing authorization token cookies between 2 projects.
It's working fine locally but not working while hosted on Azure. Getting following error:
Access to XMLHttpRequest at 'https://devplatform.b2clogin.com/devplatform.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/authorize?client_id=XXXX&redirect_uri=https://myapps.example.com/home/index ...' (redirected from 'https://www.example.com/api/home/getlist') from origin 'https://www.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I'm using following OpenIdConnect for Azure AD B2C Authentication in multiple projects.
public void ConfigureAuth(IAppBuilder app) {
// Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions() {
//locally it'll keep redirecting infinitely
CookieDomain = ".example.com" //only required when we host it to keep persist cookies
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(Globals.WellKnownMetadata, Globals.Tenant, Globals.DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = Globals.ClientId,
RedirectUri = Globals.RedirectUri,
PostLogoutRedirectUri = Globals.RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications {
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
// Specify the claim type that specifies the Name property.
TokenValidationParameters = new TokenValidationParameters {
NameClaimType = "name",
ValidateIssuer = false
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"openid profile offline_access {Globals.ReadTasksScope} {Globals.WriteTasksScope}"
}
);
}
If I do redirection on authentication failed event, get the nonce error as follows:
IDX21323: RequireNonce is ‘[PII is hidden by default. Set the
‘ShowPII’ flag in IdentityModelEventSource.cs to true to reveal it.]’.
OpenIdConnectProtocolValidationContext.Nonce was null,
OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The
nonce cannot be validated. If you don’t need to check the nonce, set
OpenIdConnectProtocolValidator.RequireNonce to ‘false’. Note if a
‘nonce’ is found it will be evaluated.
I can disable it by following statement:
ProtocolValidator = new OpenIdConnectProtocolValidator() { RequireNonce = false }
But it introduced following error:
IDX21329: RequireState is '[PII is hidden]' but the
OpenIdConnectProtocolValidationContext.State is null. State cannot be
validated.
I tried to disable that by following statement but still same issue.
ProtocolValidator = new OpenIdConnectProtocolValidator() { RequireNonce = false, RequireState = false }

Failed to get AccessToken via authorization code using MSAL 1.1.0-preview in asp.net core

I followed official steps as below to try the scenario "web app calling a Web API in Azure Ad B2C", the only difference is I am using Asp.Net core. I am using AuthorizationCode to get the access token, but it always returns with id token and NULL access token.
Create an Azure AD B2C tenant.
Register a web api.
Register a web app.
Set up policies.
Grant the web app permissions to use the web api.
My code:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme,
AutomaticChallenge = true,
ClientId = aadB2cSettings.ClientId,
MetadataAddress = $"{aadB2cSettings.Instance}{aadB2cSettings.Tenant}/v2.0/.well-known/openid-configuration?p={aadB2cSettings.B2cSignUpOrSignInPolicy}",
PostLogoutRedirectUri = aadB2cSettings.RedirectUrl,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
var authCode = context.TokenEndpointRequest.Code;
var b2cAuthority = $"{aadB2cSettings.Instance}tfp/{aadB2cSettings.Tenant}/{aadB2cSettings.B2cSignUpOrSignInPolicy}/v2.0/.well-known/openid-configuration";
var cca = new ConfidentialClientApplication(
aadB2cSettings.ClientId,
b2cAuthority,
aadB2cSettings.RedirectUrl,
new ClientCredential(aadB2cSettings.ClientSecret),
new TokenCache(),
null);
try
{
var authResult = await cca.AcquireTokenByAuthorizationCodeAsync(authCode, new[] { "https://hulab2c.onmicrosoft.com/b2cdemo/all" });
context.HandleCodeRedemption(authResult.AccessToken, authResult.IdToken);
}
catch (Exception ex)
{
throw ex;
}
}
},
Used fiddler to capture the request, it is:
POST
https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_signuporsignin
HTTP/1.1
Request Body:
client_id=1ff91f47-08ee-4973-83f4-379ad7e0679c&client_info=1&client_secret=......&scope=https%3A%2F%2Fhulab2c.onmicrosoft.com%2Fb2cdemo%2Fall+offline_access+openid+profile&grant_type=authorization_code&code=......&redirect_uri=https%3A%2F%2Flocalhost%3A44383%2F
Return:
{"id_token":"......","token_type":"Bearer","not_before":1494494423,"client_info":"......","scope":""}
So only id token, no access token. But we should get access token here, right?
Finally found out my failure reason: the request to get AuthorizationCode doesn't contain the target scope. Reflect in code, for OpenIdConnectOption in aspnetcore, the Scope parameter is readonly and its default value is "opened profile".
Scope is readonly in OpenIdConnectOption
So the default authorization code request sent is:
GET
https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_signuporsignin&client_id=7f865ca0-271e-4f27-be21-6f0072fe3ad7&redirect_uri=https%3A%2F%2Flocalhost%3A44355%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile&response_mode=form_post&nonce=......
HTTP/1.1
Thus, using this authorization code in response to get token, even we set right scope in the token request, we still can't get the access code but only id token, because the provide authorization code is only for "openid profile".
To fix this, we need to add target web api scope into the authorization code as well. Here is the how-to-fix code:
Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Scope += $" offline_access {myapiscope}";
return Task.FromResult(0);
},
......
}
In AspNet, we don't need to do this because its scope is not readonly as aspnetcore and can be set directly:
new OpenIdConnectAuthenticationOptions
{
......
Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}"
}
https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi/issues/4 Microsoft have reproduced the issue and working on fix

Resources