How to use IJwtValidator.Validate / TryValidate?
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!
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, спасибо!
Sounds like a plan, will dive into it...
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);
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?
I'll try :-)