Search code examples
jwtidentityserver4

How to validate a jwt token released from IdentityServer4


With the help of this guide i created one IdentityServer4 on localhost:5000

https://identityserver4.readthedocs.io/en/latest/

this the discovery document:

{
"issuer": "http://localhost:5000",
"jwks_uri": "http://localhost:5000/.well-known/openid-configuration/jwks",
"authorization_endpoint": "http://localhost:5000/connect/authorize",
"token_endpoint": "http://localhost:5000/connect/token",
"userinfo_endpoint": "http://localhost:5000/connect/userinfo",
"end_session_endpoint": "http://localhost:5000/connect/endsession",
"check_session_iframe": "http://localhost:5000/connect/checksession",
"revocation_endpoint": "http://localhost:5000/connect/revocation",
"introspection_endpoint": "http://localhost:5000/connect/introspect",
"device_authorization_endpoint": "http://localhost:5000/connect/deviceauthorization",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"scopes_supported": [
"profile",
"openid",
"swg_entitlements",
"offline_access"
],
"claims_supported": [
"website",
"picture",
"profile",
"preferred_username",
"nickname",
"middle_name",
"given_name",
"family_name",
"name",
"gender",
"birthdate",
"zoneinfo",
"locale",
"updated_at",
"sub"
],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"implicit",
"password",
"urn:ietf:params:oauth:grant-type:device_code"
],
"response_types_supported": [
"code",
"token",
"id_token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"response_modes_supported": [
"form_post",
"query",
"fragment"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"subject_types_supported": [
"public"
],
"code_challenge_methods_supported": [
"plain",
"S256"
],
"request_parameter_supported": true
}

i would like to validate the access token outside the IS

based on this article https://devblogs.microsoft.com/aspnet/jwt-validation-and-authorization-in-asp-net-core/ i wrote this code into a standard .net application:

http://localhost:5000/ is the IdentityServer http://localhost:5001/ is the Api project

this is the code:

void CheckToken()
{
        string token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IlRWUFNScTNWMFZibHIyN0VoY1V2U3ciLCJ0eXAiOiJhdCtqd3QifQ.eyJuYmYiOjE1OTI0ODY3MDksImV4cCI6MTU5MjQ5MDMwOSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoic3dnX2VudGl0bGVtZW50cyIsImNsaWVudF9pZCI6Im12YyIsInN1YiI6ImZkYjEiLCJhdXRoX3RpbWUiOjE1OTI0ODY3MDcsImlkcCI6ImxvY2FsIiwicm9sZSI6WyJkYXRhRXZlbnRSZWNvcmRzLmFkbWluIiwiZGF0YUV2ZW50UmVjb3Jkcy51c2VyIl0sInVzZXJuYW1lIjoiZmRiMSIsInVzZXJJZGVudGl0eSI6InZiM3luN2dEMjRCRWxmT1BaUE1qTUM3NVFLWlJWek10ZU9PVDRHdXk5TFk9Iiwic2NvcGUiOlsicHJvZmlsZSIsIm9wZW5pZCIsInN3Z19lbnRpdGxlbWVudHMiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsicHdkIl19.TCi12FLD1oLK9A7sP9aTWqBwrdOm7HiIkCwy0OJHDQsTkKs4kMtKrZcZCOfI3FErgEEvlTAlwT9t5ERKtF1Nvr9343GfcDMNRWY6Z3KGiKgskB983uOENoZZ3Hr72OOEttwK-e3Y01LuudHVNoYaX4zwX8RXTBVGu9NVOhQpksGj8uqljyxzS5ulO3wb73TEX3Z6dAClGrme-zbvc5fN4zvzfWu43fVBIbDcaiqWephWGxyK2iyyeQpMH8Om0OhWKV68vQ5H4yoE8fFWq0LWA4uRvMoAVYL6DOfzWjIF1ZeJWtD0yFxC0h9aspWj9bJVFa4GMtUsF1hkzzs9hKXw8g";

        //read secretKey from the table ClientSecrets of the IdentityServer configuration database
        const string secretKey = "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=";
        var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(secretKey));
        var handler = new JwtSecurityTokenHandler();
        var validationParameters = new TokenValidationParameters
        {
          ValidAudience = "http://localhost:5001/",
            ValidIssuer = "http://localhost:5000/",
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            LifetimeValidator = LifetimeValidator,
            IssuerSigningKey = securityKey
        };
        System.Security.Principal.IPrincipal userName = handler.ValidateToken(token, validationParameters, out _);
    }

this the error in ValidateToken:

Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: 'IDX10501: Signature validation failed. Unable to match key: kid: 'System.String'. Exceptions caught: 'System.Text.StringBuilder'. token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.'


Solution

  • This sample code validates a token against your IdentiyServer. Just pass your JWT-token string (that starts with ey) to it and the clientID. Issuer is the URL to your IdentiyServer.

    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;
        }
    }