Rename expiration mode for clarity

This is based on feedback from Brock and Dom
This commit is contained in:
Joe DeCock 2026-01-22 15:20:37 -06:00
parent 68806859c8
commit 46a9dab6c3
6 changed files with 28 additions and 28 deletions

View file

@ -40,9 +40,9 @@ public sealed class DPoPOptions
/// <summary>
/// Controls how the issued at time of proof tokens is validated. Defaults to <see
/// cref="ExpirationMode.IssuedAt"/>.
/// cref="DPoPProofExpirationMode.IssuedAt"/>.
/// </summary>
public ExpirationMode ProofTokenExpirationMode { get; set; } = ExpirationMode.IssuedAt;
public DPoPProofExpirationMode ProofTokenExpirationMode { get; set; } = DPoPProofExpirationMode.IssuedAt;
/// <summary>
/// The maximum allowed length of a proof token, which is enforced to

View file

@ -6,14 +6,14 @@ namespace Duende.AspNetCore.Authentication.JwtBearer.DPoP;
/// <summary>
/// Controls how the issued at time of proof tokens is validated.
/// </summary>
public enum ExpirationMode
public enum DPoPProofExpirationMode
{
/// <summary>
/// Validate the time from the server-issued nonce.
/// Validate the expiration time from the server-issued nonce.
/// </summary>
Nonce,
/// <summary>
/// Validate the time from the iat claim in the proof token.
/// Validate the expiration time from the iat claim in the proof token.
/// </summary>
IssuedAt,
/// <summary>

View file

@ -384,8 +384,8 @@ internal class DPoPProofValidator : IDPoPProofValidator
}
// get the largest skew based on how the client's freshness is validated
var validateIat = dPoPOptions.ProofTokenExpirationMode != ExpirationMode.Nonce;
var validateNonce = dPoPOptions.ProofTokenExpirationMode != ExpirationMode.IssuedAt;
var validateIat = dPoPOptions.ProofTokenExpirationMode != DPoPProofExpirationMode.Nonce;
var validateNonce = dPoPOptions.ProofTokenExpirationMode != DPoPProofExpirationMode.IssuedAt;
var skew = TimeSpan.Zero;
if (validateIat && dPoPOptions.ProofTokenIssuedAtClockSkew > skew)
{
@ -412,7 +412,7 @@ internal class DPoPProofValidator : IDPoPProofValidator
{
var dPoPOptions = OptionsMonitor.Get(context.Scheme);
var validateIat = dPoPOptions.ProofTokenExpirationMode != ExpirationMode.Nonce;
var validateIat = dPoPOptions.ProofTokenExpirationMode != DPoPProofExpirationMode.Nonce;
if (validateIat)
{
ValidateIat(context, result);
@ -422,7 +422,7 @@ internal class DPoPProofValidator : IDPoPProofValidator
}
}
var validateNonce = dPoPOptions.ProofTokenExpirationMode != ExpirationMode.IssuedAt;
var validateNonce = dPoPOptions.ProofTokenExpirationMode != DPoPProofExpirationMode.IssuedAt;
if (validateNonce)
{
ValidateNonce(context, result);
@ -441,7 +441,7 @@ internal class DPoPProofValidator : IDPoPProofValidator
DPoPProofValidationResult result)
{
// iat is required by an earlier validation, so result.IssuedAt will not be null
if (IsExpired(context, result.IssuedAt!.Value, ExpirationMode.IssuedAt))
if (IsExpired(context, result.IssuedAt!.Value, DPoPProofExpirationMode.IssuedAt))
{
result.SetError("Invalid 'iat' value.");
}
@ -473,19 +473,19 @@ internal class DPoPProofValidator : IDPoPProofValidator
/// Validates the expiration of the DPoP proof.
/// Returns true if the time is beyond the allowed limits, false otherwise.
/// </summary>
internal bool IsExpired(DPoPProofValidationContext context, long time, ExpirationMode mode)
internal bool IsExpired(DPoPProofValidationContext context, long time, DPoPProofExpirationMode mode)
{
// It is the responsibility of the caller to call this method with either ExpirationMode.IssuedAt or ExpirationMode.Nonce.
// Even if the configured ExpirationMode is Both, we are validating one or the other at this point, and the
// caller should make two calls, passing the more specific modes.
if (mode == ExpirationMode.Both)
if (mode == DPoPProofExpirationMode.Both)
{
throw new ArgumentException("IsExpired should be called with a specific mode (IssuedAt or Nonce), not Both.", nameof(mode));
}
var dpopOptions = OptionsMonitor.Get(context.Scheme);
var validityDuration = dpopOptions.ProofTokenLifetime;
var skew = mode == ExpirationMode.Nonce ? dpopOptions.ProofTokenNonceClockSkew
var skew = mode == DPoPProofExpirationMode.Nonce ? dpopOptions.ProofTokenNonceClockSkew
: dpopOptions.ProofTokenIssuedAtClockSkew;
return ExpirationValidator.IsExpired(validityDuration, skew, time);

View file

@ -162,7 +162,7 @@ public class DPoPIntegrationTests
[Fact]
public async Task missing_nonce_generates_server_issued_nonce()
{
ApiOptions = opt => opt.ProofTokenExpirationMode = ExpirationMode.Nonce;
ApiOptions = opt => opt.ProofTokenExpirationMode = DPoPProofExpirationMode.Nonce;
await Initialize();
var token = await LoginAndGetToken();
Api.HttpClient.SetToken("DPoP", token.AccessToken);
@ -203,7 +203,7 @@ public class DPoPIntegrationTests
ApiOptions = opt =>
{
opt.ProofTokenExpirationMode = ExpirationMode.Nonce;
opt.ProofTokenExpirationMode = DPoPProofExpirationMode.Nonce;
};
Api.OnConfigureServices += services =>
{

View file

@ -133,10 +133,10 @@ public class FreshnessTests : DPoPProofValidatorTestBase
}
}
[Theory]
[InlineData(ClockSkew, 0, ExpirationMode.IssuedAt)]
[InlineData(0, ClockSkew, ExpirationMode.Nonce)]
[InlineData(ClockSkew, 0, DPoPProofExpirationMode.IssuedAt)]
[InlineData(0, ClockSkew, DPoPProofExpirationMode.Nonce)]
public void use_client_or_server_clock_skew_depending_on_validation_mode(int clientClockSkew, int serverClockSkew,
ExpirationMode mode)
DPoPProofExpirationMode mode)
{
Options.ProofTokenIssuedAtClockSkew = TimeSpan.FromSeconds(clientClockSkew);
Options.ProofTokenNonceClockSkew = TimeSpan.FromSeconds(serverClockSkew);
@ -182,15 +182,15 @@ public class FreshnessTests : DPoPProofValidatorTestBase
}
[Theory]
[InlineData(ExpirationMode.IssuedAt)]
[InlineData(ExpirationMode.Both)]
public void validate_iat_when_option_is_set(ExpirationMode mode)
[InlineData(DPoPProofExpirationMode.IssuedAt)]
[InlineData(DPoPProofExpirationMode.Both)]
public void validate_iat_when_option_is_set(DPoPProofExpirationMode mode)
{
Options.ProofTokenExpirationMode = mode;
Options.ProofTokenLifetime = TimeSpan.FromSeconds(ValidFor);
Options.ProofTokenIssuedAtClockSkew = TimeSpan.FromSeconds(ClockSkew);
Result.IssuedAt = IssuedAt;
if (mode == ExpirationMode.Both)
if (mode == DPoPProofExpirationMode.Both)
{
Options.ProofTokenNonceClockSkew = TimeSpan.FromSeconds(ClockSkew);
Result.Nonce = DataProtector.Protect(IssuedAt.ToString());
@ -209,15 +209,15 @@ public class FreshnessTests : DPoPProofValidatorTestBase
}
[Theory]
[InlineData(ExpirationMode.Nonce)]
[InlineData(ExpirationMode.Both)]
public void validate_nonce_when_option_is_set(ExpirationMode mode)
[InlineData(DPoPProofExpirationMode.Nonce)]
[InlineData(DPoPProofExpirationMode.Both)]
public void validate_nonce_when_option_is_set(DPoPProofExpirationMode mode)
{
Options.ProofTokenExpirationMode = mode;
Options.ProofTokenLifetime = TimeSpan.FromSeconds(ValidFor);
Options.ProofTokenNonceClockSkew = TimeSpan.FromSeconds(ClockSkew);
Result.Nonce = DataProtector.Protect(IssuedAt.ToString());
if (mode == ExpirationMode.Both)
if (mode == DPoPProofExpirationMode.Both)
{
Result.IssuedAt = IssuedAt;
}

View file

@ -29,8 +29,8 @@ public class ReplayTests : DPoPProofValidatorTestBase
{
ReplayCache.ExistsFunc = _ => false;
Options.ProofTokenExpirationMode = (validateIat && validateNonce) ? ExpirationMode.Both
: validateIat ? ExpirationMode.IssuedAt : ExpirationMode.Nonce;
Options.ProofTokenExpirationMode = (validateIat && validateNonce) ? DPoPProofExpirationMode.Both
: validateIat ? DPoPProofExpirationMode.IssuedAt : DPoPProofExpirationMode.Nonce;
Options.ProofTokenIssuedAtClockSkew = TimeSpan.FromSeconds(clientClockSkew);
Options.ProofTokenNonceClockSkew = TimeSpan.FromSeconds(serverClockSkew);
Options.ProofTokenLifetime = TimeSpan.FromSeconds(ValidFor);