Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What is the meaning of 'defaultAlgorithm'? #548

Open
ycrumeyrolle opened this issue Feb 16, 2021 · 1 comment
Open

What is the meaning of 'defaultAlgorithm'? #548

ycrumeyrolle opened this issue Feb 16, 2021 · 1 comment
Labels
question Further information is requested

Comments

@ycrumeyrolle
Copy link
Collaborator

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 #545 (comment)

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:

  • A review of the name of the methods & parameters should be done. defaultAlgorithm is a perfect example of name without clear intention.
  • Some methods exposed for "legacy usage" are more disturbing than expected. Require***ByDefault, RequireAlgorithm. Marking this methods as obsolete might be an option.
@ycrumeyrolle ycrumeyrolle changed the title The fact is that X/K, under some fonts can easily be exchanged... What is the meaning of 'defaultAlgorithm'? Feb 16, 2021
@olivier-spinelli
Copy link
Contributor

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:

  • A single service called IApplicationIdentity that reference a ILocalIdentity and a set of IRemoteParty. ILocalIdentity and IRemoteParty are interface ITrustedParty { string PartyName {get;} }:
/// <summary>
/// Primary interface that encapsulates an application identity and its trusted parties.
/// This identity is theoretically independent of a process or an application domain but
/// in practice, an identity is tied to an <see cref="AppDomain"/>.
/// </summary>
public interface IApplicationIdentity
{
    /// <summary>
    /// Gets this domain application's identity.
    /// </summary>
    ILocalIdentity Local { get; }

    /// <summary>
    /// Gets the available trusted parties.
    /// </summary>
    IReadOnlyDictionary<JsonEncodedText, IRemoteParty> TrustedParties { get; }

    /// <summary>
    /// Writes a signed Json Web token binary representation of claims for a target <paramref name="audience"/> (that can be
    /// this <see cref="Local"/> itself).
    /// </summary>
    /// <param name="claims">The payload.</param>
    /// <param name="audience">The target audience.</param>
    /// <param name="output">The binary output.</param>
    /// <param name="options">Options that defaults to <see cref="JwtWriteOptions.Default"/>.</param>
    void Sign( IEnumerable<KeyValuePair<string, object>> claims, ITrustedParty audience, IBufferWriter<byte> output, JwtWriteOptions? options = null );

    /// <summary>
    /// Writes an encrypted and signed token binary representation of claims for a target <paramref name="audience"/> (that can be
    /// this <see cref="Local"/> itself).
    /// </summary>
    /// <param name="claims">The payload.</param>
    /// <param name="audience">The target audience.</param>
    /// <param name="output">The binary output.</param>
    /// <param name="options">Options that defaults to <see cref="JwtWriteOptions.Default"/>.</param>
    void Encrypt( IEnumerable<KeyValuePair<string, object>> claims, ITrustedParty audience, IBufferWriter<byte> output, JwtWriteOptions? options = null );

    /// <summary>
    /// Attempts to validate a Json Web Token.
    /// </summary>
    /// <param name="monitor">The monitor to use.</param>
    /// <param name="utf8Token">The token.</param>
    /// <returns>The <see cref="ValidatedJwt"/> for which <see cref="ValidatedJwt.Success"/> can be false.</returns>
    ValidatedJwt Validate( IActivityMonitor monitor, ReadOnlySpan<byte> utf8Token );
}

The ValidatedJwt is... minimalist:

/// <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...

@ycrumeyrolle ycrumeyrolle added the question Further information is requested label Apr 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants