using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace Yavsc.Server.Helpers;
///
/// Shared service registration helpers for Yavsc runtime services (Api, Blogs, Org, ...).
///
/// Conventions: every service reads its CORS origin allow-list from
/// Site:CorsAllowedOrigins as a JSON array, and its JWT Bearer authority
/// from Site:Authority. These helpers enforce that contract so a service
/// only needs to opt-in via a single line.
///
public static class ServiceExtensions
{
///
/// Default policy name used across all Yavsc runtime services.
///
public const string DefaultCorsPolicyName = "default";
///
/// Register the shared "default" CORS policy, sourcing the allow-list
/// from Site:CorsAllowedOrigins. Fails closed (no origins registered)
/// when the array is missing or empty.
///
/// The service collection to add CORS to.
/// Configuration root, used to read Site:CorsAllowedOrigins.
/// Optional policy name override (defaults to ).
/// The same instance for chaining.
public static IServiceCollection AddYavscCors(
this IServiceCollection services,
IConfiguration configuration,
string policyName = DefaultCorsPolicyName)
{
var allowedOrigins = configuration
.GetSection("Site:CorsAllowedOrigins")
.Get() ?? Array.Empty();
services.AddCors(options =>
{
options.AddPolicy(policyName, policy =>
{
if (allowedOrigins.Length == 0)
{
// Fail closed: with no origins configured, don't fall back to "*".
// The policy ends up effectively denying cross-origin requests,
// which is the safe default.
return;
}
policy.WithOrigins(allowedOrigins)
.AllowAnyHeader()
.AllowAnyMethod();
});
});
return services;
}
///
/// Register the standard Yavsc JWT Bearer authentication scheme, sourcing
/// the authority from Site:Authority. Throws at startup if the
/// configuration is missing — this is intentional, we'd rather fail to
/// boot than accept tokens from an unconfigured issuer.
///
/// The authentication builder to extend.
/// Configuration root, used to read Site:Authority.
/// Optional callback for service-specific options
/// (e.g. setting options.Audience in Yavsc.Org).
/// Optional scheme name override (defaults to "Bearer").
/// The same authentication builder, for chaining.
public static AuthenticationBuilder AddYavscJwtBearer(
this AuthenticationBuilder builder,
IConfiguration configuration,
Action? configure = null,
string schemeName = "Bearer")
{
var authority = configuration.GetSection("Site")["Authority"]
?? throw new InvalidOperationException(
"Site:Authority is required to configure Yavsc JWT Bearer authentication.");
return builder.AddJwtBearer(schemeName, options =>
{
options.IncludeErrorDetails = true;
options.Authority = authority;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
RoleClaimType = YavscConstants.RoleClaimType
};
options.MapInboundClaims = true;
configure?.Invoke(options);
});
}
}