auth: fix JWT default scheme and multi-audience validation
Some checks failed
Dotnet build and test / log-the-inputs (push) Has been cancelled
Dotnet build and test / build (push) Has been cancelled

This commit is contained in:
Paul Schneider 2026-07-12 14:53:17 +01:00
commit 9f061277c7

View file

@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
namespace Yavsc.Server.Helpers; namespace Yavsc.Server.Helpers;
@ -80,25 +81,36 @@ public static class ServiceExtensions
?? throw new InvalidOperationException( ?? throw new InvalidOperationException(
"Site:Authority is required to configure Yavsc JWT Bearer authentication."); "Site:Authority is required to configure Yavsc JWT Bearer authentication.");
var audiences = configuration.GetSection("Site").GetSection("Audience").Get<string[]>()
string[] audiences = configuration.GetSection("Site").GetSection("Audience").Get<string[]>() ?? Array.Empty<string>(); ?? Array.Empty<string>();
AuthenticationBuilder result = builder; if (audiences.Length == 0)
foreach (var audience in audiences)
{ {
result = builder.AddJwtBearer(schemeName, options => throw new InvalidOperationException(
"Site:Audience must contain at least one value to configure Yavsc JWT Bearer authentication.");
}
// Defensive: if the caller used AddAuthentication() without defaults,
// make sure JWT challenge/authenticate has a valid fallback scheme.
builder.Services.PostConfigure<AuthenticationOptions>(options =>
{
options.DefaultAuthenticateScheme ??= schemeName;
options.DefaultChallengeScheme ??= schemeName;
});
var result = builder.AddJwtBearer(schemeName, options =>
{ {
options.IncludeErrorDetails = true; options.IncludeErrorDetails = true;
options.Authority = authority; options.Authority = authority;
options.TokenValidationParameters = new TokenValidationParameters options.TokenValidationParameters = new TokenValidationParameters
{ {
ValidateAudience = true, ValidateAudience = true,
ValidAudience = audience, ValidAudiences = audiences,
RoleClaimType = YavscConstants.RoleClaimType, RoleClaimType = YavscConstants.RoleClaimType,
NameClaimType = YavscConstants.NameClaimType, NameClaimType = YavscConstants.NameClaimType,
}; };
options.MapInboundClaims = true; options.MapInboundClaims = true;
options.ClaimsIssuer = authority; options.ClaimsIssuer = authority;
options.Audience = audience; options.Audience = audiences[0];
// Dev: every Yavsc resource service (Yavsc.Api, Yavsc.Blogs, // Dev: every Yavsc resource service (Yavsc.Api, Yavsc.Blogs,
// Yavsc.Org itself) validates JWTs against the OP that runs // Yavsc.Org itself) validates JWTs against the OP that runs
@ -124,7 +136,6 @@ public static class ServiceExtensions
}; };
} }
}); });
}
return result; return result;