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
Related
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
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;
}
}
}
};
}
Im having an issue with a web app in azure. I use active directory to control access which works well, but one of my users gets this error message when they login in
idx10214 audience validation failed did not match validationparameters.validaudience or validationparameters null
does anyone know what this means ? Is there a workaround ?
heres how I setup openid to authorize users, how would i include the audience settings here ? What should the audience value be ?
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = AuthorityCHP,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = loURL,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential credential = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(AuthorityCHP, new ADALTokenCache(signedInUserID));
var newuri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, newuri, credential, graphResourceId);
return Task.FromResult(0);
}
}
});
Ive added some additional code in the hope it might work, but after i deployed it, i got the user affecetd to try logging in again and its still the same. Im not consuming a webapi its just a straightforwrsd webapp with a standard login page, Im really stumped, why is it only one particular user thats affecetd by this ? Can anynoe help ?
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = clientId
},
Tenant = tenantId,
AuthenticationType = "OAuth2Bearer"
});
That's mean you were calling the API using the incorrect token. When we call a web API which protected by Azure AD, it will verify the signature of the token and claims in the token.
The audience is used to which resource the token is able to access. We should acquire the token based on the resource. For example, if we protect the web API with code below, we should use the audience config in the below to acquire the token.
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
});
//app.UsePasswordAuthentication();
}
update
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
// In a real application you would use IssuerValidator for additional checks, like making sure the user's organization has signed up for your app.
// IssuerValidator = (issuer, token, tvp) =>
// {
// //if(MyCustomTenantValidation(issuer))
// return issuer;
// //else
// // throw new SecurityTokenInvalidIssuerException("Invalid issuer");
// },
},
OpenIdConnectResponseTypes has codeidtoken ,idtoken and it doesnt contain code as response type. Does UseOpenIdConnectAuthentication in OWIN support Authorization Code grant? By default it sets the responsetype as Code IDToken. Can someone share the sample for Authorization code grant using OWIN ?
From source code of Katana (below code could be found in OpenIDConnectAuthenticationHandler.AuthenticateCoreAsync method):
// code is only accepted with id_token, in this version, hence check for code is inside this if
// OpenIdConnect protocol allows a Code to be received without the id_token
if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken))
{
_logger.WriteWarning("The id_token is missing.");
return null;
}
Above code shows Microsoft.Owin.Security.OpenIdConnect library doesn't support Authorization Code grant . Though not directly supported, you can also use the hybrid flow , but it's up to you to implement the token request part , please refer to below code which use code to exchange the access token for resource protected by azure ad :
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
//
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
//
AuthorizationCodeReceived = async (context) =>
{
var code = context.Code;
// Create a Client Credential Using an Application Key
ClientCredential credential = new ClientCredential(clientId, appKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst(
"http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
}
}
}
I have setup 3rd party login providers (google and microsoft) on Azure for my web app. I am adding a custom login also (in case the user doesn't have one of those accounts). I have the token being generated as follows:
private JwtSecurityToken GenerateToken(User user)
{
var audience = ConfigurationManager.AppSettings["ValidAudience"];
var issuer = ConfigurationManager.AppSettings["ValidIssuer"];
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, user.AzureUserID) },
GetSigningKey(),
audience,
issuer,
TimeSpan.FromHours(1));
return token;
}
Where GetSigningKey() returns a string of the Azure environment variable WEBSITE_AUTH_SIGNING_KEY. The default authentication with my API controllers is working great, but when I need to refresh my token due to it expiring I am having trouble validating its signature. There are a lot of examples out there on how to validate JwtSecurityTokens, but I haven't been able to find any when you signed it using the WEBSITE_AUTH_SIGNING_KEY. Here is what I have tried to get working, without success:
var validationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ValidAudience"],
ValidIssuer = ConfigurationManager.AppSettings["ValidIssuer"],
IssuerSigningToken = <I think I need this, but not sure how to populate it...>,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true
};
SecurityToken validatedToken;
tokenHandler.ValidateToken(expiredAccessToken, validationParameters, out validatedToken);
Does anyone have any ideas on how to validate the token?
App Settings (including the signing key you mentioned) appear as environment variables, so you can either use the ConfigurationManager to read it or you can use the normal environment variable reading for ASP.NET
Try:
ConfigurationManager.AppSettings["WEBSITE_AUTH_SIGNING_KEY"]
Digging through the unit tests on GitHub for the azure-mobile-apps-net-server project, I found an AppServiceTokenHandler that does the trick:
// Validate that the token was generated by our server.
var validationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ValidAudience"],
ValidIssuer = ConfigurationManager.AppSettings["ValidIssuer"],
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = false // The token is valid for this test case whether or not it is expired.
};
ClaimsPrincipal claimsPrincipal;
if (AppServiceTokenHandler.TryValidateToken(validationParameters, expiredAccessToken, GetSigningKey(), out claimsPrincipal))
{
// Token is validated
}