Fix and cleanup for Events.WebApi
This commit is contained in:
54
Events-WebApi/Events.Auth/AuthSetupExtensions.cs
Normal file
54
Events-WebApi/Events.Auth/AuthSetupExtensions.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace Events.Auth;
|
||||
|
||||
public static class AuthSetupExtensions
|
||||
{
|
||||
public static void SetupAuthenticationAndAuthorization(this IServiceCollection services, string authority, string audience)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(authority);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(audience);
|
||||
|
||||
Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
||||
|
||||
services.AddScoped<IClaimsTransformation, ScopeClaimsTransformation>();
|
||||
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(opt =>
|
||||
{
|
||||
opt.Authority = authority;
|
||||
opt.Audience = audience;
|
||||
opt.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateAudience = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
NameClaimType = ClaimTypes.NameIdentifier
|
||||
};
|
||||
opt.Events = new JwtBearerEvents
|
||||
{
|
||||
OnAuthenticationFailed = context =>
|
||||
{
|
||||
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
|
||||
{
|
||||
context.Response.Headers.Append("Token-Expired", "true");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
foreach (var policy in Policies.All)
|
||||
{
|
||||
options.AddPolicy(policy.Key, policy.Value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
17
Events-WebApi/Events.Auth/Events.Auth.csproj
Normal file
17
Events-WebApi/Events.Auth/Events.Auth.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
21
Events-WebApi/Events.Auth/Policies.cs
Normal file
21
Events-WebApi/Events.Auth/Policies.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Events.Auth;
|
||||
|
||||
public static class Policies
|
||||
{
|
||||
public static IEnumerable<KeyValuePair<string, Action<AuthorizationPolicyBuilder>>> All
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new KeyValuePair<string, Action<AuthorizationPolicyBuilder>>(nameof(ReadData), ReadData);
|
||||
yield return new KeyValuePair<string, Action<AuthorizationPolicyBuilder>>(nameof(EditData), EditData);
|
||||
}
|
||||
}
|
||||
|
||||
public static Action<AuthorizationPolicyBuilder> ReadData =>
|
||||
policy => policy.RequireClaim("scope", "events:read");
|
||||
|
||||
public static Action<AuthorizationPolicyBuilder> EditData =>
|
||||
policy => policy.RequireClaim("scope", "events:write");
|
||||
}
|
||||
47
Events-WebApi/Events.Auth/ScopeClaimsTransformation.cs
Normal file
47
Events-WebApi/Events.Auth/ScopeClaimsTransformation.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace Events.Auth;
|
||||
|
||||
public sealed class ScopeClaimsTransformation : IClaimsTransformation
|
||||
{
|
||||
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
|
||||
{
|
||||
if (principal.Identity is not ClaimsIdentity identity || !identity.IsAuthenticated)
|
||||
{
|
||||
return Task.FromResult(principal);
|
||||
}
|
||||
|
||||
Claim[] combinedScopeClaims = identity
|
||||
.FindAll("scope")
|
||||
.Where(claim => claim.Value.Contains(' '))
|
||||
.ToArray();
|
||||
|
||||
if (combinedScopeClaims.Length == 0)
|
||||
{
|
||||
return Task.FromResult(principal);
|
||||
}
|
||||
|
||||
var additionalIdentity = new ClaimsIdentity();
|
||||
|
||||
foreach (Claim combinedClaim in combinedScopeClaims)
|
||||
{
|
||||
foreach (string scope in combinedClaim.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||
{
|
||||
if (identity.HasClaim("scope", scope) || additionalIdentity.HasClaim("scope", scope))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
additionalIdentity.AddClaim(new Claim("scope", scope, combinedClaim.ValueType, combinedClaim.Issuer));
|
||||
}
|
||||
}
|
||||
|
||||
if (additionalIdentity.Claims.Any())
|
||||
{
|
||||
principal.AddIdentity(additionalIdentity);
|
||||
}
|
||||
|
||||
return Task.FromResult(principal);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user