jwt icon indicating copy to clipboard operation
jwt copied to clipboard

How to use IJwtValidator.Validate / TryValidate?

Open CADBIMDeveloper opened this issue 1 year ago • 5 comments

Hi experts!

I'm trying to use the tokens from the service, which provides an API endpoint to obtain a list of current public signing keys in the following format:

{
    "keys": [
        {
            "kty": "RSA",
            "kid": "64DOW2rh8Omn3ivMMSLe4d6Tq0Q_RS256",
            "use": "sig",
            "n": "wutSjmSqM6OWXXRVcwp6NCp-ZP5kUf8RmLuDF5fANzRjjmCkLpcS08lZHwFvCWppmgEwUDDKtPEHs00S4mY3zIgP3wZ-xJqUOlSxppUrs868EGBA4am8GeYXXlVw8vc0LLnF0-QSS3kinsyd7ZfiPu9DwXw01RlYwnjfok5khhcoKuPqlhZ-aqXxoOU6o3qRlJF1WvVNeIdMp2rdgOnNHsfTgHsNVHRAmy4iDtxF1AcaQTuhbLVEZzg1Z8QTxnoG5URX7UjQ6ankEbops8S0Bu6mBqQtHGszD7zRk1q0bXu4qmEsDhMfZzzhZDbPVRfTKuASJPRc-k0V8oEx8T5HQQ",
            "e": "AQAB"
        },
        ... other keys
    ]
}

First I'm creating a JwtDecoder without the validator, retrieving the header:

{
  "alg": "RS256",
  "kid": "64DOW2rh8Omn3ivMMSLe4d6Tq0Q_RS256",
  "pi.atm": "assc"
}

As far as I understand, I must find a key from the list by kid, probably take the value of the n field and feed JwtValidator.Validate / JwtValidator.TryValidate with the payload I get from the decoder.Decode and this key. However, JwtValidator.Validate method has 3 parameters. What should I pass to this method?

Thank you!

CADBIMDeveloper avatar Jan 19 '24 14:01 CADBIMDeveloper

hi @CADBIMDeveloper, привет!

kid is a feature which isn't used (or at least - discussed often). From what I could remember:

You need to use DelegateAlgorithmFactory.Create() which accepts Func<JwtDecoderContext and another IJwtAlgorithm. Inside the lambda you'll be able to use JwtHeader to select the right key/cert and pas it to the underlying factory. Makes sense?

abatishchev avatar Jan 19 '24 17:01 abatishchev

Привет, @abatishchev, спасибо!

Sounds like a plan, will dive into it...

CADBIMDeveloper avatar Jan 23 '24 07:01 CADBIMDeveloper

So, for the others who is struggling with the same kind of problems, the solution could be something like that:

The definition of JWKS (JSON Web Key Set):

internal sealed class JsonWebKey
{
    [JsonPropertyName("kid")]
    public string KeyId { get; set; } = null!;

    [JsonPropertyName("n")]
    public string Modulus { get; set; } = null!;

    [JsonPropertyName("e")]
    public string Exponent { get; set; } = null!;
}

internal sealed class JsonWebKeySet
{
    public IEnumerable<JsonWebKey> Keys { get; set; } = null!;
}

[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(JsonWebKeySet))]
internal partial class JsonWebKeySetContext : JsonSerializerContext;

The JWT algorithm factory implementation:

internal sealed class AlgorithmFactory : IAlgorithmFactory
{
    private readonly Dictionary<string, JsonWebKey> keys;

    public AlgorithmFactory(JsonWebKeySet keySet) => keys = keySet.Keys.ToDictionary(x => x.KeyId);

    public IJwtAlgorithm Create(JwtDecoderContext context)
    {
        if (!keys.TryGetValue(context.Header.KeyId, out var key))
            throw new SignatureVerificationException("The key is missing or invalid");

        var rsaParameters = new RSAParameters
        {
            Modulus = WebEncoders.Base64UrlDecode(key.Modulus),
            Exponent = WebEncoders.Base64UrlDecode(key.Exponent)
        };

        return new RS256Algorithm(RSA.Create(rsaParameters));
    }
}

And the code sample how to use it:

var serializer = new SystemTextSerializer();
var validator = new JwtValidator(serializer, new UtcDateTimeProvider());

var keySet = JsonSerializer.Deserialize(yourKeySet, JsonWebKeySetContext.Default.JsonWebKeySet)!;

var algorithmFactory = new DelegateAlgorithmFactory(new AlgorithmFactory(keySet));
var decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithmFactory);

var body = decoder.Decode(yourToken);

CADBIMDeveloper avatar Jan 23 '24 10:01 CADBIMDeveloper

Happy to hear it worked for you!

It looks like the library is missing something / would benefit if you could contribute back the additions to the model. Would you please?

abatishchev avatar Jan 23 '24 17:01 abatishchev

I'll try :-)

CADBIMDeveloper avatar Jan 23 '24 17:01 CADBIMDeveloper