Skip to content
4 changes: 2 additions & 2 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ Please review our [code of conduct](CODE_OF_CONDUCT.md).

## Our Development Process
We use GitHub with a simple GitFlow inspired flow.
All new features and/or fixes are merged into the `development` branch by creating a Pull Request.
All new features and/or fixes are merged into the `main` branch by creating a Pull Request.

## Pull Requests
We actively welcome your pull requests.

1. Fork the repo and create your branch from `development`
1. Fork the repo and create your branch from `main`
2. If you've added code that should be tested, add tests (DO follow [Microsoft Engineering Guidelines](https://github.com/dotnet/aspnetcore/wiki/Engineering-guidelines))
3. Any changes or additions requires documentation in the form of documenting public members
4. Ensure that all existing as well as new test passes
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pipelines.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Cuemon CI/CD Pipeline
on:
pull_request:
pull_request_target:
branches: [main]
paths-ignore:
- .codecov/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,63 @@

namespace Cuemon.AspNetCore.Authentication.Basic
{
/// <summary>
/// Provides a HTTP Basic Authentication implementation of <see cref="AuthenticationHandler{TOptions}"/> for ASP.NET Core.
/// </summary>
/// <seealso cref="AuthenticationHandler{TOptions}" />
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="BasicAuthenticationHandler"/> class.
/// </summary>
/// <param name="options">The monitor for the options instance.</param>
/// <param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILoggerFactory" />.</param>
/// <param name="encoder">The <see cref="T:System.Text.Encodings.Web.UrlEncoder" />.</param>
/// <param name="clock">The <see cref="T:Microsoft.AspNetCore.Authentication.ISystemClock" />.</param>
public BasicAuthenticationHandler(IOptionsMonitor<BasicAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
/// <summary>
/// Provides a HTTP Basic Authentication implementation of <see cref="AuthenticationHandler{TOptions}"/> for ASP.NET Core.
/// </summary>
/// <seealso cref="AuthenticationHandler{TOptions}" />
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
#if NET6_0
/// <summary>
/// Initializes a new instance of the <see cref="BasicAuthenticationHandler"/> class.
/// </summary>
/// <param name="options">The monitor for the options instance.</param>
/// <param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILoggerFactory" />.</param>
/// <param name="encoder">The <see cref="T:System.Text.Encodings.Web.UrlEncoder" />.</param>
/// <param name="clock">The <see cref="T:Microsoft.AspNetCore.Authentication.ISystemClock" />.</param>
public BasicAuthenticationHandler(IOptionsMonitor<BasicAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
#else
/// <summary>
/// Initializes a new instance of the <see cref="BasicAuthenticationHandler"/> class.
/// </summary>
/// <param name="options">The monitor for the options instance.</param>
/// <param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILoggerFactory" />.</param>
/// <param name="encoder">The <see cref="T:System.Text.Encodings.Web.UrlEncoder" />.</param>
public BasicAuthenticationHandler(IOptionsMonitor<BasicAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
{
}
#endif

/// <summary>
/// Handle authenticate as an asynchronous operation.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> representing the asynchronous operation.</returns>
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
Context.Items.TryAdd(nameof(BasicAuthenticationOptions), Options);
/// <summary>
/// Handle authenticate as an asynchronous operation.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> representing the asynchronous operation.</returns>
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
Context.Items.TryAdd(nameof(BasicAuthenticationOptions), Options);

if (!Authenticator.TryAuthenticate(Context, Options.RequireSecureConnection, BasicAuthenticationMiddleware.AuthorizationHeaderParser, BasicAuthenticationMiddleware.TryAuthenticate, out var principal))
if (!Authenticator.TryAuthenticate(Context, Options.RequireSecureConnection, BasicAuthenticationMiddleware.AuthorizationHeaderParser, BasicAuthenticationMiddleware.TryAuthenticate, out var principal))
{
var unathorized = new UnauthorizedException(Options.UnauthorizedMessage, principal.Failure);
return Task.FromResult(AuthenticateResult.Fail(unathorized));
}
return Task.FromResult(AuthenticateResult.Fail(unathorized));
}

var ticket = new AuthenticationTicket(principal.Result, BasicAuthorizationHeader.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}

var ticket = new AuthenticationTicket(principal.Result, BasicAuthorizationHeader.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}

/// <summary>
/// Handle challenge as an asynchronous operation.
/// </summary>
/// <param name="properties">The properties.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
/// Handle challenge as an asynchronous operation.
/// </summary>
/// <param name="properties">The properties.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
AuthenticationHandlerFeature.Set(await HandleAuthenticateOnceSafeAsync().ConfigureAwait(false), Context); // so annoying that Microsoft does not propagate AuthenticateResult properly - other have noticed as well: https://github.com/dotnet/aspnetcore/issues/44100
Decorator.Enclose(Response.Headers).TryAdd(HeaderNames.WWWAuthenticate, string.Create(CultureInfo.InvariantCulture, $"{BasicAuthorizationHeader.Scheme} realm=\"{Options.Realm}\""));
Decorator.Enclose(Response.Headers).TryAdd(HeaderNames.WWWAuthenticate, string.Create(CultureInfo.InvariantCulture, $"{BasicAuthorizationHeader.Scheme} realm=\"{Options.Realm}\""));
await base.HandleChallengeAsync(properties).ConfigureAwait(false);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,63 +12,77 @@

namespace Cuemon.AspNetCore.Authentication.Digest
{
/// <summary>
/// Provides a HTTP Digest Access Authentication implementation of <see cref="AuthenticationHandler{TOptions}"/> for ASP.NET Core.
/// </summary>
/// <seealso cref="AuthenticationHandler{TOptions}" />
public class DigestAuthenticationHandler : AuthenticationHandler<DigestAuthenticationOptions>
{
private readonly INonceTracker _nonceTracker;
/// <summary>
/// Provides a HTTP Digest Access Authentication implementation of <see cref="AuthenticationHandler{TOptions}"/> for ASP.NET Core.
/// </summary>
/// <seealso cref="AuthenticationHandler{TOptions}" />
public class DigestAuthenticationHandler : AuthenticationHandler<DigestAuthenticationOptions>
{
private readonly INonceTracker _nonceTracker;

#if NET6_0
/// <summary>
/// Initializes a new instance of the <see cref="DigestAuthenticationHandler"/> class.
/// </summary>
/// <param name="options">The monitor for the options instance.</param>
/// <param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILoggerFactory" />.</param>
/// <param name="encoder">The <see cref="T:System.Text.Encodings.Web.UrlEncoder" />.</param>
/// <param name="clock">The <see cref="T:Microsoft.AspNetCore.Authentication.ISystemClock" />.</param>
/// <param name="nonceTracker">The dependency injected implementation of an <see cref="INonceTracker"/>.</param>
public DigestAuthenticationHandler(IOptionsMonitor<DigestAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, INonceTracker nonceTracker = null) : base(options, logger, encoder, clock)
{
_nonceTracker = nonceTracker;
}
#else
/// <summary>
/// Initializes a new instance of the <see cref="DigestAuthenticationHandler"/> class.
/// </summary>
/// <param name="options">The monitor for the options instance.</param>
/// <param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILoggerFactory" />.</param>
/// <param name="encoder">The <see cref="T:System.Text.Encodings.Web.UrlEncoder" />.</param>
/// <param name="clock">The <see cref="T:Microsoft.AspNetCore.Authentication.ISystemClock" />.</param>
/// <param name="nonceTracker">The dependency injected implementation of an <see cref="INonceTracker"/>.</param>
public DigestAuthenticationHandler(IOptionsMonitor<DigestAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, INonceTracker nonceTracker = null) : base(options, logger, encoder, clock)
public DigestAuthenticationHandler(IOptionsMonitor<DigestAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, INonceTracker nonceTracker = null) : base(options, logger, encoder)
{
_nonceTracker = nonceTracker;
}
#endif

/// <summary>
/// Handle authenticate as an asynchronous operation.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> representing the asynchronous operation.</returns>
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
Context.Items.TryAdd(nameof(DigestAuthenticationOptions), Options);
Context.Items.TryAdd(nameof(INonceTracker), _nonceTracker);
/// <summary>
/// Handle authenticate as an asynchronous operation.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> representing the asynchronous operation.</returns>
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
Context.Items.TryAdd(nameof(DigestAuthenticationOptions), Options);
Context.Items.TryAdd(nameof(INonceTracker), _nonceTracker);

if (!Authenticator.TryAuthenticate(Context, Options.RequireSecureConnection, DigestAuthenticationMiddleware.AuthorizationHeaderParser, DigestAuthenticationMiddleware.TryAuthenticate, out var principal))
{
if (!Authenticator.TryAuthenticate(Context, Options.RequireSecureConnection, DigestAuthenticationMiddleware.AuthorizationHeaderParser, DigestAuthenticationMiddleware.TryAuthenticate, out var principal))
{
var unathorized = new UnauthorizedException(Options.UnauthorizedMessage, principal.Failure);
return Task.FromResult(AuthenticateResult.Fail(unathorized));
}
return Task.FromResult(AuthenticateResult.Fail(unathorized));
}

var ticket = new AuthenticationTicket(principal.Result, DigestAuthorizationHeader.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
/// <summary>
/// Handle challenge as an asynchronous operation.
/// </summary>
/// <param name="properties">The properties.</param>½
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <remarks><c>qop</c> is included and supported to be compliant with RFC 2617 (hence, this implementation cannot revert to reduced legacy RFC 2069 mode).</remarks>
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
string etag = Response.Headers[HeaderNames.ETag];
if (string.IsNullOrEmpty(etag)) { etag = "no-entity-tag"; }
var opaqueGenerator = Options.OpaqueGenerator;
var nonceSecret = Options.NonceSecret;
var nonceGenerator = Options.NonceGenerator;
var staleNonce = Context.Items[DigestFields.Stale] as string ?? "false";
var ticket = new AuthenticationTicket(principal.Result, DigestAuthorizationHeader.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}

/// <summary>
/// Handle challenge as an asynchronous operation.
/// </summary>
/// <param name="properties">The properties.</param>½
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <remarks><c>qop</c> is included and supported to be compliant with RFC 2617 (hence, this implementation cannot revert to reduced legacy RFC 2069 mode).</remarks>
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
string etag = Response.Headers[HeaderNames.ETag];
if (string.IsNullOrEmpty(etag)) { etag = "no-entity-tag"; }
var opaqueGenerator = Options.OpaqueGenerator;
var nonceSecret = Options.NonceSecret;
var nonceGenerator = Options.NonceGenerator;
var staleNonce = Context.Items[DigestFields.Stale] as string ?? "false";
AuthenticationHandlerFeature.Set(await HandleAuthenticateOnceSafeAsync().ConfigureAwait(false), Context); // so annoying that Microsoft does not propagate AuthenticateResult properly - other have noticed as well: https://github.com/dotnet/aspnetcore/issues/44100
Decorator.Enclose(Response.Headers).TryAdd(HeaderNames.WWWAuthenticate, string.Create(CultureInfo.InvariantCulture, $"{DigestAuthorizationHeader.Scheme} realm=\"{Options.Realm}\", qop=\"auth, auth-int\", nonce=\"{nonceGenerator(DateTime.UtcNow, etag, nonceSecret())}\", opaque=\"{opaqueGenerator()}\", stale=\"{staleNonce}\", algorithm=\"{DigestAuthenticationMiddleware.ParseAlgorithm(Options.Algorithm)}\""));
Decorator.Enclose(Response.Headers).TryAdd(HeaderNames.WWWAuthenticate, string.Create(CultureInfo.InvariantCulture, $"{DigestAuthorizationHeader.Scheme} realm=\"{Options.Realm}\", qop=\"auth, auth-int\", nonce=\"{nonceGenerator(DateTime.UtcNow, etag, nonceSecret())}\", opaque=\"{opaqueGenerator()}\", stale=\"{staleNonce}\", algorithm=\"{DigestAuthenticationMiddleware.ParseAlgorithm(Options.Algorithm)}\""));
await base.HandleChallengeAsync(properties).ConfigureAwait(false);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

namespace Cuemon.AspNetCore.Authentication.Hmac
{
/// <summary>
/// Provides a HTTP HMAC Authentication implementation of <see cref="AuthenticationHandler{TOptions}"/> for ASP.NET Core.
/// </summary>
/// <seealso cref="AuthenticationHandler{TOptions}" />
public class HmacAuthenticationHandler : AuthenticationHandler<HmacAuthenticationOptions>
{
/// <summary>
/// Provides a HTTP HMAC Authentication implementation of <see cref="AuthenticationHandler{TOptions}"/> for ASP.NET Core.
/// </summary>
/// <seealso cref="AuthenticationHandler{TOptions}" />
public class HmacAuthenticationHandler : AuthenticationHandler<HmacAuthenticationOptions>
{
#if NET6_0
/// <summary>
/// Initializes a new instance of the <see cref="HmacAuthenticationHandler"/> class.
/// </summary>
Expand All @@ -26,35 +27,46 @@ public class HmacAuthenticationHandler : AuthenticationHandler<HmacAuthenticatio
public HmacAuthenticationHandler(IOptionsMonitor<HmacAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
#else
/// <summary>
/// Initializes a new instance of the <see cref="HmacAuthenticationHandler"/> class.
/// </summary>
/// <param name="options">The monitor for the options instance.</param>
/// <param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILoggerFactory" />.</param>
/// <param name="encoder">The <see cref="T:System.Text.Encodings.Web.UrlEncoder" />.</param>
public HmacAuthenticationHandler(IOptionsMonitor<HmacAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
{
}
#endif

/// <summary>
/// Handle authenticate as an asynchronous operation.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> representing the asynchronous operation.</returns>
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
Context.Items.TryAdd(nameof(HmacAuthenticationOptions), Options);
/// <summary>
/// Handle authenticate as an asynchronous operation.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> representing the asynchronous operation.</returns>
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
Context.Items.TryAdd(nameof(HmacAuthenticationOptions), Options);

if (!Authenticator.TryAuthenticate(Context, Options.RequireSecureConnection, HmacAuthenticationMiddleware.AuthorizationHeaderParser, HmacAuthenticationMiddleware.TryAuthenticate, out var principal))
{
if (!Authenticator.TryAuthenticate(Context, Options.RequireSecureConnection, HmacAuthenticationMiddleware.AuthorizationHeaderParser, HmacAuthenticationMiddleware.TryAuthenticate, out var principal))
{
var unathorized = new UnauthorizedException(Options.UnauthorizedMessage, principal.Failure);
return Task.FromResult(AuthenticateResult.Fail(unathorized));
}
return Task.FromResult(AuthenticateResult.Fail(unathorized));
}

var ticket = new AuthenticationTicket(principal.Result, Options.AuthenticationScheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
var ticket = new AuthenticationTicket(principal.Result, Options.AuthenticationScheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}

/// <summary>
/// Handle challenge as an asynchronous operation.
/// </summary>
/// <param name="properties">The properties.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
/// <summary>
/// Handle challenge as an asynchronous operation.
/// </summary>
/// <param name="properties">The properties.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
AuthenticationHandlerFeature.Set(await HandleAuthenticateOnceSafeAsync().ConfigureAwait(false), Context); // so annoying that Microsoft does not propagate AuthenticateResult properly - other have noticed as well: https://github.com/dotnet/aspnetcore/issues/44100
Decorator.Enclose(Response.Headers).TryAdd(HeaderNames.WWWAuthenticate, Options.AuthenticationScheme);
Decorator.Enclose(Response.Headers).TryAdd(HeaderNames.WWWAuthenticate, Options.AuthenticationScheme);
await base.HandleChallengeAsync(properties).ConfigureAwait(false);
}
}
}
}
}
Loading