uruk-project / Jwt

JSON Web Token implementation for .Net & .Net Core
MIT License
85 stars 11 forks source link

What is the meaning of 'defaultAlgorithm'? #548

Open ycrumeyrolle opened 3 years ago

ycrumeyrolle commented 3 years ago

I'm having a hard time figuring out the "defaultAlgorithm" here:

TokenValidationPolicyBuilder RequireSignature(string issuer, Jwks keys, SignatureAlgorithm defaultAlgorithm) Because the "alg" header parameter (https://tools.ietf.org/html/rfc7515#section-4.1.1):

This Header Parameter MUST be present and MUST be understood and processed by implementations.

... default here seems rather strange to me. Is it a way to REQUIRE a given algorithm? In this case, how does this fit with the multiple keys that could be associated to the "kid"?

(This defaultAlgorithm is not optional... just like the "alg" parameter.)

Sorry to ask but I'll appreciate some explanations about this... Thanks in advance.

Originally posted by @olivier-spinelli in https://github.com/uruk-project/Jwt/issues/545#issuecomment-779937175

This API is new in v2.0, still in beta, so there may be some remaining polish on the public API. The excepted usage is:

            var policy = new TokenValidationPolicyBuilder()
                           .RequireSignature("https://idp.example.com/", key, SignatureAlgorithm.HS256)
                           .RequireAudience("636C69656E745F6964")
                           .Build();

By doing so, there is a link between the issuer, the key, and the signature algorithm. The issuer is used as a lookup for finding the required validation. This is the common usage of a JWS: You have an issuer, a key, and an associated signature algorithm. So this is not a default algorithm.

It is also possible to do so (legacy usage):

            var policy = new TokenValidationPolicyBuilder()
                           .DefaultIssuer("https://idp.example.com/")
                           .RequireSignatureByDefault(key, SignatureAlgorithm.HS256)
                           .Build();

The default issuer is not used as lookup, but only as the last chance to validate an issuer, as-well as the signature key. This allow some use cases where there is no issuer.

Your question convinces me on 2 points:

olivier-spinelli commented 3 years ago

Cool! I'm not crazy! So let me explain what I'm trying to do: a maximal encapsulation of all this JWT stuff to be used by "Application developpers". I'm looking for the simplest API that can be imagined.

My approach so far is the following one:

/// <summary>
/// Minimalist definition of a token validation result.
/// </summary>
/// <remarks>
/// There is no status or other error description: validation failures often require a lot of details
/// and should be extensible (to handle specific rules and validators): we simply use the <see cref="CK.Core.ActivityMonitor"/>
/// to log any validation details (warnings or errors).
/// </remarks>
public readonly struct ValidatedJwt
{
    readonly ReadOnlyMemory<byte>? _payload;

    internal ValidatedJwt( ReadOnlyMemory<byte>? payload, bool enc )
    {
        _payload = payload;
        IsEncrypted = enc;
    }

    /// <summary>
    /// Gets whether the token validation succeeded.
    /// </summary>
    public bool Success => _payload is not null;

    /// <summary>
    /// Gets whether the token has been validated and was encrypted.
    /// </summary>
    public bool IsEncrypted { get; }

    /// <summary>
    /// Gets the raw Utf8 encoded payload.
    /// This is null when <see cref="Success"/> is false.
    /// </summary>
    /// <returns>The payload text or null if <see cref="Success"/> is false.</returns>
    public ReadOnlyMemory<byte>? Payload => _payload;
}

Key renewal, exchanges (and bindings to explicit certificates like PEM, PKCS#12, etc.) take place under the hood (via other abstractions), but the whole idea is here: this easy to use ApplicationIdentity somehow implements/encapsulates your https://github.com/uruk-project/Jwt/blob/master/docs/Security_considerations.md.

It's hard to make simple things...