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). Asymmetric signing algorithm is always more secure in preventing the token to be tampered with compared to a symmetric algorithm since the private key is always kept at the Identity Provider (IDP) and the token consumer only has access to the public key to verify the token signature. In certain situations, for instance the token consumer can’t handle asymmetric key signing, it may be desirable to have an Azure AD generated JWT token signed with a symmetric key. This blog will show you:

  1. How to configure Azure AD to issue a symmetric key signing id_token for a particular application and
  2. How to use an OpenID Connect ASP.Net application to validate this symmetrically signed id_token

Configuring an Azure AD App Registration with signing secret

  1. Create an App Registration in Azure AD. For reply URL, use the appropriate reply URL from your application.
  2. Take note of both the Object ID and the Application (client) ID from the Application Overview blade


  1. Configure this application with a signing secret

For this part, I am using the following values to demonstrate the concept. An application signing key in Azure AD is encapsulated in the keyCredentials attribute. Each keyCredential object has a CustomKeyIdentifier and a key attribute. See https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-app-manifest#keycredentials-attribute for more info.


CustomKeyIdentifier: “My Cool Key
Secret used for signing (aka key): “SuperSecretPassword2

There are 2 ways to configure a secret key for signing:

  1. Use Azure AD PowerShell commandlet New-AzureADApplicationKeyCredential as below:
# Log in to Azure AD tenant
Connect-AzureAD
# Create a symmetric signing key with a value of SuperSecretPassword2
New-AzureADApplicationKeyCredential -ObjectId <Application Object ID> -CustomKeyIdentifier "My Cool Key" -Type Symmetric -Usage Sign -Value "SuperSecretPassword2" -StartDate "10/24/2020" -EndDate "9/13/2021"
  1. or use Microsoft Graph to update this Application with the signing key secret.

For this part, we will need to Base64-Encode both the secret and the CustomKeyIdentifier. The MS Graph request and request body should be similar to the following:

PATCH https://graph.microsoft.com/v1.0/applications/<Application Object ID>
Request Body:
{
"keyCredentials": [{
     "customKeyIdentifier": "TXkgQ29vbCBLZXk=",  // base64 encoded string "My Cool Key"
     "displayName": "My symmetric key",
     "endDateTime": "2021-09-13T00:00:00Z",
     "keyId": "31d6106a-f8d8-415b-a15b-a1dcd64e6841",
     "startDateTime": "2020-10-24T07:25:00Z",
     "type": "Symmetric",
     "usage": "Sign",
     "key": "U3VwZXJTZWNyZXRQYXNzd29yZDI="  // base64 encoded string "SuperSecretPassword2"
}]
}

You can perform this step using MS Graph Explorer tool:

Note: OpenID Connect OWIN middleware expects a signing symmetric key to have a minimum length of 16 characters. You might see the errors similar to the following if the signing secret is too short:

IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey , KeyId:
Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: kg2LYs2T0CTjIfj4rt6JIynen38
Microsoft.IdentityModel.Tokens.RsaSecurityKey , KeyId: kg2LYs2T0CTjIfj4rt6JIynen38
Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: 18pnMg3UmrWvBK_tkDAbjgM5CmA
Microsoft.IdentityModel.Tokens.RsaSecurityKey , KeyId: 18pnMg3UmrWvBK_tkDAbjgM5CmA
'.
Exceptions caught:
'System.ArgumentOutOfRangeException: IDX10603: Decryption failed. Keys tried: 'HS256'.
Exceptions caught:
'128'.
token: '64'
Parameter name: KeySize
at Microsoft.IdentityModel.Tokens.SymmetricSignatureProvider..ctor(SecurityKey key, String algorithm, Boolean willCreateSignatures) in C:\agent2_work\56\s\src\Microsoft.IdentityModel.Tokens\SymmetricSignatureProvider.cs:line 47
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures) in C:\agent2_work\56\s\src\Microsoft.IdentityModel.Tokens\CryptoProviderFactory.cs:line 446
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm) in C:\agent2_work\56\s\src\Microsoft.IdentityModel.Tokens\CryptoProviderFactory.cs:line 249

Once a symmetric signing key is configured, your id_token received from Azure AD V1 endpoint for this app should look similar to the following in https://jwt.ms:

Configuring OpenID Connect OWIN middleware to use this symmetric key:

For this part I followed the tutorial at https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-asp-webapp to create an ASP.Net MVC Web Application and made some minor changes to the Token Validation Parameters to configure to use the above symmetric key. This project uses the Application ID, reply URL, and tenant ID of the application we create at the beginning of this post. Below is my complete Startup.cs file. The key to this is to set the IssuerSigningKey field in the TokenValidationParameters object.

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Notifications;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;

[assembly: OwinStartup(typeof(NetWebAppOIDC.Startup))]

namespace NetWebAppOIDC
{
    public class Startup
    {
        // Appllication ID of the registered app
        string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
        // you can use the home page of this app for redirect URI. Make sure it's the same in App Registration reply URL
        string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
        // Directory ID
        static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
        string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);

        public void Configuration(IAppBuilder app)
        {
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
            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,
                    RedirectUri = redirectUri,
                    // PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
                    PostLogoutRedirectUri = redirectUri,
                    Scope = OpenIdConnectScope.OpenIdProfile,
                    // ResponseType is set to request the id_token - which contains basic information about the signed-in user
                    ResponseType = OpenIdConnectResponseType.IdToken,
                    // ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
                    // To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
                    // To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
                    
                    TokenValidationParameters = new TokenValidationParameters()
                    {
                         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuperSecretPassword2"))
                    },
                   
                    // OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
                    
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        AuthenticationFailed = OnAuthenticationFailed
                    }
                    
                    
                }
            );
            Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
        }
        private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
        {
            context.HandleResponse();
            context.Response.Redirect("/?errormessage=" + context.Exception.Message);
            return Task.FromResult(0);
        }
    }
}

Without a valid symmetric key, you might run into the following Signature Validation error:

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: kg2LYs2T0CTjIfj4rt6JIynen38
Microsoft.IdentityModel.Tokens.RsaSecurityKey , KeyId: kg2LYs2T0CTjIfj4rt6JIynen38
Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: M6pX7RHoraLsprfJeRCjSxuURhc
Microsoft.IdentityModel.Tokens.RsaSecurityKey , KeyId: M6pX7RHoraLsprfJeRCjSxuURhc
Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: 18pnMg3UmrWvBK_tkDAbjgM5CmA
Microsoft.IdentityModel.Tokens.RsaSecurityKey , KeyId: 18pnMg3UmrWvBK_tkDAbjgM5CmA
'.
Exceptions caught:
'System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.
Algorithm: 'HS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.X509SecurityKey'
is not supported.
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures) in C:\agent2_work\56\s\src\Microsoft.IdentityModel.Tokens\CryptoProviderFactory.cs:line 373
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm) in C:\agent2_work\56\s\src\Microsoft.IdentityModel.Tokens\CryptoProviderFactory.cs:line 249

References

Secure Your ASP.NET Web Forms Application with OpenID Connect and Okta

How to Validate Apigee Edge generated JWT Token from .NET/C# code?

Error: IDX10603: Decryption failed. Keys tried: [PII is hidden].Parameter name: KeySize

RS256 vs HS256: What’s the difference?

JWT: Symmetic and Asymmetic Key Authentication In ASP.NET Core

Leave a Comment