Skip to content

Commit

Permalink
fix(authentication): combine JWT Bearer and Cookies in BearerOrCookie…
Browse files Browse the repository at this point in the history
…s policy scheme and use it by default
  • Loading branch information
undrcrxwn committed Oct 29, 2024
1 parent dab1614 commit 9e45ede
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ is AuthenticationConstants.CookieAuthenticationUserIdClaim

return Guid.TryParse(userIdClaim?.Value, out var value) ? value : null;
}

public static Guid GetRequiredUserId(this ClaimsPrincipal principal) =>
principal.GetUserId() ?? throw new ForbiddenException();
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,21 @@ public static IServiceCollection ConfigureAuthentication(this IServiceCollection
var dataProtectionRedisMultiplexer = ConnectionMultiplexer.Connect(dataProtectionRedisConnectionString);
services.AddDataProtection().PersistKeysToStackExchangeRedis(dataProtectionRedisMultiplexer);

var builder = services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
var builder = services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = "BearerOrCookies";
sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
});

builder.AddPolicyScheme("BearerOrCookies", "Bearer or Cookies", options =>
{
options.ForwardDefaultSelector = context =>
context.Request.Headers.ContainsKey("Authorization")
? JwtBearerDefaults.AuthenticationScheme
: CookieAuthenticationDefaults.AuthenticationScheme;
});

builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
builder.AddJwtBearer(options =>
{
options.MapInboundClaims = false;
options.RequireHttpsMetadata = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ public async Task<ActionResult<CommentResponse>> Reply([FromRoute] Guid parentCo
[ProducesResponseType<ProblemDetails>(Status403Forbidden)]
[ProducesResponseType<ProblemDetails>(Status404NotFound)]
[ProducesResponseType<ProblemDetails>(Status500InternalServerError)]
public async Task React([FromRoute] Guid discussionId, [FromBody] ISet<string> reactions) =>
public async Task<IActionResult> React([FromRoute] Guid discussionId, [FromBody] ISet<string> reactions)
{
await reactionsService.SetAsync(discussionId, User.GetRequiredUserId(), reactions);
return NoContent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public async Task<DiscussionResponse> Update([FromRoute] Guid discussionId, [Fro
[ProducesResponseType<ProblemDetails>(Status403Forbidden)]
[ProducesResponseType<ProblemDetails>(Status404NotFound)]
[ProducesResponseType<ProblemDetails>(Status500InternalServerError)]
public async Task React([FromRoute] Guid discussionId, [FromBody] ISet<string> reactions) =>
public async Task<IActionResult> React([FromRoute] Guid discussionId, [FromBody] ISet<string> reactions)
{
await reactionsService.SetAsync(discussionId, User.GetRequiredUserId(), reactions);
return NoContent();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Net;
using System.Net.Http.Json;
using CrowdParlay.Social.Domain.DTOs;

namespace CrowdParlay.Social.IntegrationTests.Tests;

public class AuthenticationTests(WebApplicationContext context) : IAssemblyFixture<WebApplicationContext>

Check warning on line 7 in tests/CrowdParlay.Social.IntegrationTests/Tests/AuthenticationTests.cs

View workflow job for this annotation

GitHub Actions / Build & test

Fixture argument 'context' does not have a fixture source (if it comes from a collection definition, ensure the definition is in the same assembly as the test) (https://xunit.net/xunit.analyzers/rules/xUnit1041)

Check warning on line 7 in tests/CrowdParlay.Social.IntegrationTests/Tests/AuthenticationTests.cs

View workflow job for this annotation

GitHub Actions / Build & test

Fixture argument 'context' does not have a fixture source (if it comes from a collection definition, ensure the definition is in the same assembly as the test) (https://xunit.net/xunit.analyzers/rules/xUnit1041)
{
private readonly HttpClient _client = context.Server.CreateClient();
private readonly IServiceProvider _services = context.Services;

[Fact(DisplayName = "Create a discussion providing access JWT as Bearer token")]
public async Task CreateDiscussionWithAccessToken()
Expand All @@ -15,11 +17,37 @@ public async Task CreateDiscussionWithAccessToken()
var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/discussions");
request.Content = JsonContent.Create(new DiscussionRequest("Short title", "Long description."));
request.Headers.Add("Authorization", "Bearer " + Authorization.ProduceAccessToken(userId));

// Act
var response = await _client.SendAsync(request);

// Assert
response.Should().HaveStatusCode(HttpStatusCode.Created);
}

[Fact(DisplayName = "Search discussions returns discussions with viewer reactions")]
public async Task SearchReactionsWithAccessToken()
{
// Arrange
var authorId = Guid.NewGuid();
HashSet<string> reactions = ["\u2764\ufe0f", "\ud83c\udf08"];

await using var scope = _services.CreateAsyncScope();
var discussionsService = scope.ServiceProvider.GetRequiredService<IDiscussionsService>();
var reactionsService = scope.ServiceProvider.GetRequiredService<IReactionsService>();

var discussion = await discussionsService.CreateAsync(authorId, "Test discussion", "Description.");
await reactionsService.SetAsync(discussion.Id, authorId, reactions);

var request = new HttpRequestMessage(HttpMethod.Get, $"/api/v1/discussions?offset=0&count=10&authorId={authorId}");
request.Headers.Add("Authorization", "Bearer " + Authorization.ProduceAccessToken(authorId));

// Act
var response = await _client.SendAsync(request);

// Assert
response.Should().HaveStatusCode(HttpStatusCode.OK);
var page = await response.Content.ReadFromJsonAsync<Page<DiscussionResponse>>(GlobalSerializerOptions.SnakeCase);
page?.Items.Should().ContainSingle().Which.ViewerReactions.Should().BeEquivalentTo(reactions);
}
}

0 comments on commit 9e45ede

Please sign in to comment.