These signature validation errors are caused when the resource provider (not Azure AD) is unable to validate the signature of the token, either because the signing key could not be found or the signing key used was not able to validate the signature. This article will describe the most common scenarios and solutions. The concept and root cause is still the same and will continue to apply. Unfortunately, many developers and vendors implement their token validation differently.

First, get the access token being sent to the resource provider. We need to decode this to review (3) important claims:

  • aud
  • iss
  • kid

You can decode access tokens using https://jwt.ms

Let’s get the easy one out of the way. If you send a Microsoft Graph access token to any other resource provider that’s not Microsoft Graph, you will get a Signature validation error. Only Microsoft Graph can validate these tokens. You will know if this is a Microsoft Graph token when taking a look at the “aud” claim and its value is one of the following:

  • https://graph.microsoft.us
  • https://graph.microsoft.us/
  • https://graph.microsoft.com
  • https://graph.microsoft.com/
  • https://dod-graph.microsoft.us
  • https://dod-graph.microsoft.us/
  • 00000003-0000-0000-c000-000000000000

Make sure you are acquiring the correct access token for the resource provider and the “aud” claim is what the resource provider is expecting. The audience of access tokens is determined by the scope parameter that is sent in the request when acquiring access tokens. So if you want to get an access token for https://api.contoso.com, then the scope will look something like this: https://api.contoso.com/read

For more information about Azure AD and exposing your API

https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-expose-web-apis#:~:text=Azure%20Add%20Scope%201%20Sign%20in%20to%20the,or%20specify%20your%20own.%20Field%20…%20See%20More.

As for the other scenarios, the resource provider determines where to get the signing keys based on the OpenId Connect Metadata configuration and which signing key to use based on the “kid” claim in the access token. This is configured on the resource provider such as your custom built API or API Authentication layer. If you’re using a Microsoft Authentication library like MSAL or Microsoft Identity Web, the default is generally:

https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

If you have configured a Tenant ID that is for example “e21bbca2-1b75-4dea-9e34-d3d95d2ec661” then the MetadataAddress would be:

https://login.microsoftonline.com/e21bbca2-1b75-4dea-9e34-d3d95d2ec661/v2.0/.well-known/openid-configuration

If you configured a “Authority” that looks like https://login.microsoftonline.us/e21bbca2-1b75-4dea-9e34-d3d95d2ec661 then the MetadataAddress would be:

https://login.microsoftonline.us/e21bbca2-1b75-4dea-9e34-d3d95d2ec661/v2.0/.well-known/openid-configuration

The OpenId Connect Metadata endpoint has a property that provides the location to the signing keys. This is the jwks_uri also known as the discovery keys endpoint. So, depending on which OpenId Connect Metadata endpoint is used, it will return a URL for the jwks_uri. Here is a table that provides a few examples:

  • Example 1: 
    Metadata endpoint: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration 
    Discovery keys endpoint: https://login.microsoftonline.com/common/discovery/v2.0/keys  
  • Example 2:  
    Metadata endpoint: https://login.microsoftonline.com/e21bbca2-1b75-4dea-9e34-d3d95d2ec661/v2.0/.well-known/openid-configuration   
    Discovery keys endpoint: https://login.microsoftonline.com/e21bbca2-1b75-4dea-9e34-d3d95d2ec661/discovery/v2.0/keys   
  • Example 3: 
    Metadata endpoint: https://login.microsoftonline.us/e21bbca2-1b75-4dea-9e34-d3d95d2ec661/v2.0/.well-known/openid-configuration   
    Discovery keys endpoint: https://login.microsoftonline.us/e21bbca2-1b75-4dea-9e34-d3d95d2ec661/discovery/v2.0/keys   
  • Example 4: 
    Metadata endpoint: https://login.microsoftonline.us/e21bbca2-1b75-4dea-9e34-d3d95d2ec661/v2.0/.well-known/openid-configuration?appid=06051593-1954-4e1f-a75f-7e5de243aeff   
    Discovery keys endpoint: https://login.microsoftonline.us/e21bbca2-1b75-4dea-9e34-d3d95d2ec661/discovery/v2.0/keys?appid=06051593-1954-4e1f-a75f-7e5de243aeff  

Our discovery keys endpoint may contain more than one signing key. So, if you’re manually looking for a specific key and not the signing key provided by the access token or your caching keys, your signature validation may fail as we do frequently rotate keys.

https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-signing-key-rollover

The content of our discovery keys endpoint will look something like this:

	"keys": [
		{
			"kty": "RSA",
			"use": "sig",
			"kid": "nOo3ZDrODXEK1jKWhXslHR_KXEg",
			"x5t": "nOo3ZDrODXEK1jKWhXslHR_KXEg",
			"n": "oaLLT9hkcSj2tGf...",
			"e": "AQAB",
			"x5c": [
				"MIIDBTCCAe..."
			],
			"issuer": "https://login.microsoftonline.com/aa00d1fa-5269-4e1c-b06d-30868371d2c5/v2.0"
		},

The “kid” claim in the access token needs to match one of the available keys available on the discovery key endpoint based on the “kid” property.

If the “kid” does not match between the “kid” on the access token and the keys available on the discovery endpoint, there are two possible reasons:

  1. Azure AD and B2C uses different signing keys
  2. The application is enabled for SAML SSO.

Azure AD and B2C uses different signing keys

Next, we will take a look at the “iss” claim of the access token. For tokens issued by Azure AD, this will be one of the following values…

For tokens issued by Azure B2C, this will look something like this…

https://{your-domain}.b2clogin.com/tfp/[your-tenant-id}/{your-policy-id}/v2.0/

This is where it is really important to make sure your OpenId Connect Metadata configuration on the resource provider is configured correctly.

For tokens issued by Azure AD, make sure the OpenId Connect Metadata configuration looks something like this:

https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

For more information regarding to Azure AD OpenId Connect configurations:

https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc

For tokens issued by Azure B2C, make sure the OpenId Connect Metadata configuration looks something like this:

https://{domain}.b2clogin.com/{tenant-id}/{b2c-policy}/v2.0/.well-known/openid-configuration

For more information regarding to Azure B2C OpenId Connect configurations:

https://docs.microsoft.com/en-us/azure/active-directory-b2c/openid-connect

 

The application is enabled for SAML SSO.

Assuming the token is issued from Azure AD and not B2C, When an application in Azure AD is enabled for SAML SSO, the signing key used to sign tokens is the SAML signing certificate that was generated when SAML SSO was enabled.

So when you look for the “kid” from the access token on the default discovery keys endpoint, usually “https://login.microsoftonline.com/common/discovery/v2.0/keys”, you may not see it listed. There are a couple solutions. First and foremost you should keep your apps seperate for OAuth2 and SAML. It is not recommened to use the same app that will perform both OAuth2 and SAML.

  • Solution (1): Create a new app registration and do not enable SAML SSO (This is the recommended approach)
  • Solution (2): You can convert the Enterprise App to use OAuth2 only. To do this…
    • Disable SAML SSO (this is the preferredSingleSignOnMode property on the servicePrincipal by setting it to null or oidc.)

Examples of where to configure OpenId Connect Metadata configurations

Make sure you set the OpenId Connect Metadata configuration based on whether the token is issued from Azure AD or Azure B2C or if you need to add “?appid={appid}”

Generally if you configure the Azure AD instance and Tenant, or Authority correctly, this will resolve your issue.

Instance

Azure AD instance would be https://login.microsoftonline.com

For more information about Azure AD Instances:

https://docs.microsoft.com/en-us/azure/active-directory/develop/authentication-national-cloud

Tenant

Tenant would be contoso.onmicrosoft.com

You can also use the Directory id or any verified domain. Its recommended to use the Directory ID or the initial domain provided by Azure AD (i.e. contoso.onmicrosoft.com).

Authority

If configuring a Authority, generally Instance and Tenant is not needed as Authority follows this format:

{Instance}/{Tenant}

So, Authority would be https://login.microsoftonline.com/contoso.onmicrosoft.com

Generally the Metadata Address is built based on this Instance/Tenant/Authority configuration and will automatically concatenate “/.well-known/openid-configuration” at the end.

The following sections provide examples of manually specifying the Metadata Address.

Using Microsoft Identity Web

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                    .AddMicrosoftIdentityWebApp(
                       options =>
                       {
                           Configuration.Bind("AzureAd", options);
                           options.MetadataAddress = metadataAddress,
                           
                       })
                       .EnableTokenAcquisitionToCallDownstreamApi(options => Configuration.Bind("AzureAd", options), initialScopes)
                         .AddMicrosoftGraph(Configuration.GetSection("GraphAPI"))
                         .AddInMemoryTokenCaches();

For more information, see:

https://github.com/AzureAD/microsoft-identity-web/wiki/customization

Using Asp.Net standard framework using “UseWindowsAzureActiveDirectoryBearerAuthentication

Set the Metadata to https://login.microsoftonline.com/{tenant-id}/.well-known/openid-configuration

            app.UseWindowsAzureActiveDirectoryBearerAuthentication(
                new WindowsAzureActiveDirectoryBearerAuthenticationOptions
                {
                    MetadataAddress = metadataAddress,
                    Tenant = ConfigurationManager.AppSettings["ida:Tenant"],

Using Asp.Net standard framework using “UseOpenIdConnectAuthentication

You can either set the Authority as https://login.microsoftonline.com/{tenant-id}/v2.0

public void Configuration(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                // Sets the ClientId, authority, RedirectUri as obtained from web.config
                ClientId = clientId,
                Authority = authority,

or set the Metadata to https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration

        public void Configuration(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                // Sets the ClientId, authority, RedirectUri as obtained from web.config
                ClientId = clientId,
                MetadataAddress = metadataAddress,

Using Azure App Service Authentication

Please see the following article (Its configured using the Issuer URL):

https://docs.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad#-enable-azure-active-directory-in-your-app-service-app

Using Azure API Management

Configuring for Azure B2C: https://docs.microsoft.com/en-us/azure/active-directory-b2c/secure-api-management?tabs=app-reg-ga

Additional resources:

https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens#validating-tokens

https://openid.net/specs/openid-connect-discovery-1_0.htm

Leave a Comment