Skip to content

Commit

Permalink
Share claims principal creation logic
Browse files Browse the repository at this point in the history
- Make swagger ui work with new bearer token auth
  • Loading branch information
davidfowl committed Jul 13, 2023
1 parent 5a3c961 commit bca3b06
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 36 deletions.
20 changes: 6 additions & 14 deletions TodoApi.Tests/TokenService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.BearerToken;
using Microsoft.Extensions.Options;
Expand All @@ -23,24 +22,17 @@ public interface ITokenService

public sealed class TokenService : ITokenService
{
private BearerTokenOptions _options;
private readonly BearerTokenOptions _options;

public TokenService(IOptionsMonitor<BearerTokenOptions> options)
{
// We're reading the authentication configuration for the Bearer scheme
_options = options.Get(BearerTokenDefaults.AuthenticationScheme);
_options = options.Get(AuthenticationHelper.BearerTokenScheme);
}

public string GenerateToken(string username, bool isAdmin = false)
{
var identity = new ClaimsIdentity(BearerTokenDefaults.AuthenticationScheme);

identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, username));

if (isAdmin)
{
identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
}
var claimsPrincipal = AuthenticationHelper.CreateClaimsPrincipal(username, isAdmin);

var utcNow = (_options.TimeProvider ?? TimeProvider.System).GetUtcNow();

Expand All @@ -49,10 +41,10 @@ public string GenerateToken(string username, bool isAdmin = false)
ExpiresUtc = utcNow + _options.BearerTokenExpiration
};

var ticket = CreateBearerTicket(new ClaimsPrincipal(identity), properties);
var ticket = CreateBearerTicket(claimsPrincipal, properties);

static AuthenticationTicket CreateBearerTicket(ClaimsPrincipal user, AuthenticationProperties properties)
=> new(user, properties, $"{BearerTokenDefaults.AuthenticationScheme}:AccessToken");
=> new(user, properties, $"{AuthenticationHelper.BearerTokenScheme}:AccessToken");

return _options.BearerTokenProtector.Protect(ticket);
}
Expand Down
2 changes: 1 addition & 1 deletion TodoApi/Authentication/AuthenticationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public static class AuthenticationExtensions
{
public static WebApplicationBuilder AddAuthentication(this WebApplicationBuilder builder)
{
builder.Services.AddAuthentication().AddBearerToken();
builder.Services.AddAuthentication().AddBearerToken(AuthenticationHelper.BearerTokenScheme);

return builder;
}
Expand Down
22 changes: 22 additions & 0 deletions TodoApi/Authentication/AuthenticationHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Security.Claims;

namespace TodoApi;

public class AuthenticationHelper
{
public static readonly string BearerTokenScheme = "Bearer";

public static ClaimsPrincipal CreateClaimsPrincipal(string userName, bool isAdmin = false)
{
var identity = new ClaimsIdentity(BearerTokenScheme);

identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userName));

if (isAdmin)
{
identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
}

return new ClaimsPrincipal(identity);
}
}
12 changes: 7 additions & 5 deletions TodoApi/Extensions/OpenApiExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.BearerToken;
using Microsoft.OpenApi.Models;

namespace TodoApi;

public static class OpenApiExtensions
{
// Adds the JWT security scheme to the Open API description
// Adds the security scheme to the Open API description
public static IEndpointConventionBuilder AddOpenApiSecurityRequirement(this IEndpointConventionBuilder builder)
{
const string schemeName = "Bearer";

var scheme = new OpenApiSecurityScheme()
{
Type = SecuritySchemeType.Http,
Name = JwtBearerDefaults.AuthenticationScheme,
Scheme = JwtBearerDefaults.AuthenticationScheme,
Name = schemeName,
Scheme = schemeName,
Reference = new()
{
Type = ReferenceType.SecurityScheme,
Id = JwtBearerDefaults.AuthenticationScheme
Id = schemeName
}
};

Expand Down
1 change: 0 additions & 1 deletion TodoApi/TodoApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" />
Expand Down
20 changes: 5 additions & 15 deletions TodoApi/Users/UsersApi.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.BearerToken;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Identity;

namespace TodoApi;
Expand Down Expand Up @@ -36,9 +34,9 @@ public static RouteGroupBuilder MapUsers(this IEndpointRouteBuilder routes)
return TypedResults.BadRequest();
}
ClaimsPrincipal principal = CreateClaimsPrincipal(user);
var principal = AuthenticationHelper.CreateClaimsPrincipal(user.UserName!);
return TypedResults.SignIn(principal, authenticationScheme: BearerTokenDefaults.AuthenticationScheme);
return TypedResults.SignIn(principal);
});

group.MapPost("/token/{provider}", async Task<Results<SignInHttpResult, ValidationProblem, Ok<AccessTokenResponse>>> (string provider, ExternalUserInfo userInfo, UserManager<TodoUser> userManager) =>
Expand All @@ -61,22 +59,14 @@ public static RouteGroupBuilder MapUsers(this IEndpointRouteBuilder routes)
if (result.Succeeded)
{
ClaimsPrincipal principal = CreateClaimsPrincipal(user);
var principal = AuthenticationHelper.CreateClaimsPrincipal(user.UserName!);
return TypedResults.SignIn(principal, authenticationScheme: BearerTokenDefaults.AuthenticationScheme);
return TypedResults.SignIn(principal);
}
return TypedResults.ValidationProblem(result.Errors.ToDictionary(e => e.Code, e => new[] { e.Description }));
});

return group;
}

private static ClaimsPrincipal CreateClaimsPrincipal(TodoUser user)
{
return new ClaimsPrincipal(
new ClaimsIdentity(new[] {
new Claim(ClaimTypes.NameIdentifier, user.UserName!) },
BearerTokenDefaults.AuthenticationScheme));
}
}

0 comments on commit bca3b06

Please sign in to comment.