0
votes

Iam working on app, which consists from angular frontend and ASP net Web API backend(.net 4.5). For authentication iam using OpenIdConnect. I succesfully connected frontend to identity provider but now i need to validate id token on backend, so i can be sure, that only validated users can call backend.

This id token use rs256 algorithm for signing. So on backend, i need to do two things:

  1. Get JWKs from identity provider URL - iam a little lost here, should i get it throug normal HttpClient, or there is some library or helper function to do this?

  2. Generate RSA public key out of JWKs and validate token - for this iam using this function:

         string token="xyz..";
         RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    
         rsa.ImportParameters(
           new RSAParameters()
           {
               Modulus = FromBase64Url("xyz.."),
               Exponent = FromBase64Url("xyz..")
           });
    
         var validationParameters = new TokenValidationParameters
         {
             RequireExpirationTime = true,
             RequireSignedTokens = true,
             ValidateAudience = false,
             ValidateIssuer = false,
             ValidateLifetime = true,
             IssuerSigningKey = new RsaSecurityKey(rsa)
         };
    
         SecurityToken validatedSecurityToken = null;
         var handler = new JwtSecurityTokenHandler();
         handler.ValidateToken(tokenStr, validationParameters, out validatedSecurityToken);
         JwtSecurityToken validatedJwt = validatedSecurityToken as JwtSecurityToken;
    

It works, but now i need to connect it somehow with the loaded JWKs and register it to use this for every request that comes. Any advices or simple example would really help me. Thx.

1

1 Answers

1
votes

This code below is taken from one of my training classes and it will automatically download and validate the provided token and I hope you can use it as a reference. You typically will use the ConfigurationManager to download the IdentityServer configuration and JWKS for you. It will also internally cache and periodically (every 24) readload the config.

using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using OpenID_Connect_client.Models;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading;

namespace OpenID_Connect_client
{
    public class TokenValidator
    {
        private readonly IOpenIDSettings openIDSettings;

        public TokenValidator(IOpenIDSettings openIDSettings)
        {
            this.openIDSettings = openIDSettings;
        }

        public string ValidateToken(string token, string clientId)
        {
            try
            {
                string issuer = openIDSettings.Issuer;

                var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{issuer}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
                var openIdConfig = configurationManager.GetConfigurationAsync(CancellationToken.None).Result;

                // Configure the TokenValidationParameters. Assign the SigningKeys which were downloaded from Auth0. 
                // Also set the Issuer and Audience(s) to validate
                //https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs
                var validationParameters =
                    new TokenValidationParameters
                    {
                        IssuerSigningKeys = openIdConfig.SigningKeys,

                        ValidAudiences = new[] { clientId },
                        ValidIssuer = issuer,
                        ValidateLifetime = true,
                        ValidateAudience = true,
                        ValidateIssuer = true,
                        ValidateIssuerSigningKey = true,
                        ValidateTokenReplay = true
                    };

                // Now validate the token. If the token is not valid for any reason, an exception will be thrown by the method
                SecurityToken validatedToken;
                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                var user = handler.ValidateToken(token, validationParameters, out validatedToken);

                // The ValidateToken method above will return a ClaimsPrincipal. Get the user ID from the NameIdentifier claim
                // (The sub claim from the JWT will be translated to the NameIdentifier claim)
                return $"Token is validated. User Id {user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value}";
            }
            catch (Exception exc)
            {
                return "Invalid token: " + exc.Message;
            }
        }


    }
}