Bifrost/src/Bifrost.Web/Configuration/OidcIssuerOptions.cs

72 lines
3.2 KiB
C#

namespace Bifrost.Web.Configuration;
/// <summary>
/// The OIDC provider surface (section <c>Bifrost:Oidc</c>). An absent section means the provider is not mapped at
/// all; a present section is complete or startup fails. Keys and the client secret are named by file path, never inlined.
/// </summary>
public sealed class OidcIssuerOptions
{
public const string SectionName = "Bifrost:Oidc";
/// <summary>PEM file holding the EC P-256 private key that signs tokens (ES256).</summary>
public string SigningKeyPath { get; init; } = string.Empty;
/// <summary>File holding the 32-byte symmetric token-encryption key as base64.</summary>
public string EncryptionKeyPath { get; init; } = string.Empty;
public OidcClientsOptions Clients { get; init; } = new();
public OidcLimitsOptions Limits { get; init; } = new();
/// <summary>
/// Optional path of the issuer's brake marker. While a file exists there, /connect/* answers 503 and nothing is issued;
/// remove it to release. Checked on every request (one stat), so engaging and releasing need no restart.
/// Unset: the issuer has no marker brake (removing this section and restarting is the other off switch).
/// </summary>
public string BrakePath { get; init; } = string.Empty;
/// <summary>Null when the section is absent.</summary>
public static OidcIssuerOptions? FromConfiguration(IConfiguration configuration)
{
var section = configuration.GetSection(SectionName);
return section.Exists() ? section.Get<OidcIssuerOptions>() ?? new OidcIssuerOptions() : null;
}
}
public sealed class OidcClientsOptions
{
/// <summary>Matrix Authentication Service, the one client of this provider.</summary>
public OidcClientOptions Mas { get; init; } = new();
}
public sealed class OidcClientOptions
{
public string ClientId { get; init; } = string.Empty;
/// <summary>File holding the client secret.</summary>
public string ClientSecretPath { get; init; } = string.Empty;
public string RedirectUri { get; init; } = string.Empty;
/// <summary>MAS's back-channel logout URL for this provider. Empty: no logout is sent and no session is ended.</summary>
public string BackchannelLogoutUri { get; init; } = string.Empty;
}
/// <summary>
/// One budget on /connect/authorize, /connect/token and /connect/userinfo together, per source address in one fixed window (a refused caller gets 429 with Retry-After),
/// and the bound on what the anonymous authorize endpoint can park. No per-client budget: the client id is public. The defaults suit one client (MAS) behind one proxy.
/// </summary>
public sealed class OidcLimitsOptions
{
public int PerAddressPermits { get; init; } = 30;
public TimeSpan Window { get; init; } = TimeSpan.FromMinutes(1);
public int MaxTrackedKeys { get; init; } = 10_000;
/// <summary>The most OpenIddict token rows (parked authorize requests, codes, access tokens) allowed to exist; a new authorize request beyond it is refused until the prune frees room.</summary>
public int MaxTokenRows { get; init; } = 20_000;
/// <summary>How often expired and orphaned authorizations and tokens are deleted.</summary>
public TimeSpan PruneInterval { get; init; } = TimeSpan.FromMinutes(5);
}