Skip to content

Commit

Permalink
Additional coverage and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaliumhexacyanoferrat committed Jul 1, 2024
1 parent 6fbd0e6 commit 398098f
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 5 deletions.
6 changes: 2 additions & 4 deletions Modules/Authentication/Bearer/BearerAuthenticationConcern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,10 @@ internal BearerAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> c
{
var user = await ValidationOptions.UserMapping.Invoke(request, jwt);

if (user == null)
if (user != null)
{
throw new ProviderException(ResponseStatus.Forbidden, "Access denied for requested resource");
request.SetUser(user);
}

request.SetUser(user);
}

return await Content.HandleAsync(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,71 @@ public sealed class BearerAuthenticationConcernBuilder : IConcernBuilder

#region Functionality

/// <summary>
/// Sets the expected issuer. Tokens that are not issued by this
/// party will be declined.
/// </summary>
/// <param name="issuer">The URL of the exepcted issuer</param>
/// <remarks>
/// Setting the issuer will cause the concern to download and cache
/// the signing keys that are used to ensure that the party actually
/// issued a token.
/// </remarks>
public BearerAuthenticationConcernBuilder Issuer(string issuer)
{
_Options.Issuer = issuer;
return this;
}

/// <summary>
/// Sets the expected audience that should be accepted.
/// </summary>
/// <param name="audience">The audience to check for</param>
public BearerAuthenticationConcernBuilder Audience(string audience)
{
_Options.Audience = audience;
return this;
}

/// <summary>
/// Adds a custom validator that can analyze the token read from the
/// request and can perform additional checks.
/// </summary>
/// <param name="validator">The custom validator to be used</param>
/// <remarks>
/// This validator will be invoked after the regular checks (such as the
/// issuer) have been performed.
///
/// If you would like to deny user access within a custom validator,
/// you can throw a <see cref="ProviderException" />.
/// </remarks>
public BearerAuthenticationConcernBuilder Validation(Func<JwtSecurityToken, Task> validator)
{
_Options.CustomValidator = validator;
return this;
}

/// <summary>
/// Optionally register a function that will compute the user that
/// should be set for the request.
/// </summary>
/// <param name="userMapping">The user mapping to be used</param>
/// <remarks>
/// The usage of this mechanism allows to inject the user into
/// service methods via the user injector class. Returning null
/// within the delegate will not deny user access - if you would
/// like to prevent such user, you can throw a <see cref="ProviderException" />.
/// </remarks>
public BearerAuthenticationConcernBuilder UserMapping(Func<IRequest, JwtSecurityToken, ValueTask<IUser?>> userMapping)
{
_Options.UserMapping = userMapping;
return this;
}

/// <summary>
/// If enabled, tokens that have expired or are not valid yet are
/// still accepted. This should be used for testing purposes only.
/// </summary>
public BearerAuthenticationConcernBuilder AllowExpired()
{
_Options.Lifetime = false;
Expand Down
6 changes: 6 additions & 0 deletions Modules/Authentication/BearerAuthentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ namespace GenHTTP.Modules.Authentication
public static class BearerAuthentication
{

/// <summary>
/// Creates a concern that will read an access token from
/// the authorization headers and validate it according to
/// its configuration.
/// </summary>
/// <returns>The newly created concern</returns>
public static BearerAuthenticationConcernBuilder Create() => new();

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

using GenHTTP.Api.Content;
using GenHTTP.Api.Content.Authentication;
using GenHTTP.Api.Protocol;

using GenHTTP.Modules.Authentication;
using GenHTTP.Modules.Authentication.Bearer;
using GenHTTP.Modules.Functional;
Expand Down Expand Up @@ -78,7 +80,29 @@ public async Task TestUser()
await response.AssertStatusAsync(HttpStatusCode.OK);
}

private static async Task<HttpResponseMessage> Execute(BearerAuthenticationConcernBuilder builder, string? token)
[TestMethod]
public async Task TestNoToken()
{
var auth = BearerAuthentication.Create()
.AllowExpired();

using var response = await Execute(auth);

await response.AssertStatusAsync(HttpStatusCode.Unauthorized);
}

[TestMethod]
public async Task TestMalformedToken()
{
var auth = BearerAuthentication.Create()
.AllowExpired();

using var response = await Execute(auth, "Lorem Ipsum");

await response.AssertStatusAsync(HttpStatusCode.BadRequest);
}

private static async Task<HttpResponseMessage> Execute(BearerAuthenticationConcernBuilder builder, string? token = null)
{
var handler = Inline.Create()
.Get(() => "Secured")
Expand Down

0 comments on commit 398098f

Please sign in to comment.