feat(security): add distributed authorization foundation

This commit is contained in:
2026-07-29 10:40:10 +08:00
parent c7f9a4e3c9
commit df88fa19cb
76 changed files with 22020 additions and 88 deletions

View File

@@ -29,6 +29,15 @@ internal static class AuthenticationExtensions
var jwtOptions = configuration
.GetSection("Security:Jwt")
.Get<JwtOptions>() ?? new JwtOptions();
services.AddOptions<BrowserAuthOptions>()
.Bind(configuration.GetSection(BrowserAuthOptions.SectionName))
.Validate(
options => options.AllowedOrigins.All(origin =>
Uri.TryCreate(origin, UriKind.Absolute, out var uri) &&
(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) &&
string.IsNullOrEmpty(uri.PathAndQuery.Trim('/'))),
"BrowserAuth AllowedOrigins must contain only HTTP(S) origins without paths.")
.ValidateOnStart();
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
@@ -84,11 +93,41 @@ internal static class AuthenticationExtensions
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
if (string.IsNullOrWhiteSpace(context.Request.Headers.Authorization) &&
IsSameOriginBrowserRequest(context.Request) &&
context.Request.Cookies.TryGetValue(BrowserAuthOptions.AccessCookie, out var accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
},
OnTokenValidated = ValidateTokenAsync,
OnChallenge = WriteTenantConflictChallengeAsync
};
}
private static bool IsSameOriginBrowserRequest(HttpRequest request)
{
var source = request.Headers.Origin.ToString();
if (string.IsNullOrWhiteSpace(source))
{
source = request.Headers.Referer.ToString();
}
if (Uri.TryCreate(source, UriKind.Absolute, out var uri))
{
return string.Equals(uri.Scheme, request.Scheme, StringComparison.OrdinalIgnoreCase) &&
string.Equals(uri.Authority, request.Host.Value, StringComparison.OrdinalIgnoreCase);
}
return string.Equals(
request.Headers["Sec-Fetch-Site"].ToString(),
"same-origin",
StringComparison.OrdinalIgnoreCase);
}
private static async Task ValidateTokenAsync(TokenValidatedContext context)
{
var principal = context.Principal;