using Refit; namespace OTSSignsOrchestrator.Server.Clients; // ── Configuration ─────────────────────────────────────────────────────────── public sealed class AuthentikOptions { public const string Section = "Authentik"; public string BaseUrl { get; set; } = string.Empty; public string ApiToken { get; set; } = string.Empty; /// UUID of the OTS signing certificate-key pair used for all SAML sources. public string OtsSigningKpId { get; set; } = string.Empty; /// Authentik pre-authentication flow slug for SAML sources (e.g. "default-source-pre-authentication"). public string SourcePreAuthFlowSlug { get; set; } = "default-source-pre-authentication"; /// Authentik authentication flow slug for SAML sources (e.g. "default-source-authentication"). public string SourceAuthFlowSlug { get; set; } = "default-source-authentication"; } // ── Request DTOs ──────────────────────────────────────────────────────────── public record CreateSamlProviderRequest( string Name, string AuthorizationFlow, string AcsUrl, string Issuer, string SpBinding, string Audience, string? SigningKp); public record CreateAuthentikApplicationRequest( string Name, string Slug, string Provider, string? MetaLaunchUrl); public record CreateAuthentikGroupRequest( string Name, bool? IsSuperuser, string? Parent); public record CreateFlowRequest( string Name, bool? SingleUse, DateTimeOffset? Expires); public record CreateAuthentikUserRequest( string Username, string Name, string Email, string[] Groups); public record ImportCertRequest( string Name, string CertificateData, string? KeyData); public record CreateSamlSourceRequest( string Name, string Slug, string SsoUrl, string? SloUrl, string Issuer, string? SigningKp, string? VerificationKp, string BindingType, string NameIdPolicy, string PreAuthenticationFlow, string AuthenticationFlow, bool AllowIdpInitiated); // ── Response DTOs ─────────────────────────────────────────────────────────── /// Authentik paginated list response. Results contain dictionaries with entity fields. public record AuthentikPagedResult( List> Results); // ── Authentik Refit Interface ─────────────────────────────────────────────── // One global Authentik instance serves all tenants. [Headers("Authorization: Bearer")] public interface IAuthentikClient { // ── SAML Providers ────────────────────────────────────────────────────── [Post("/api/v3/providers/saml/")] Task>> CreateSamlProviderAsync( [Body] CreateSamlProviderRequest body); [Get("/api/v3/providers/saml/{id}/")] Task>> GetSamlProviderAsync(int id); [Delete("/api/v3/providers/saml/{id}/")] Task DeleteSamlProviderAsync(int id); // ── Applications ──────────────────────────────────────────────────────── [Post("/api/v3/core/applications/")] Task>> CreateApplicationAsync( [Body] CreateAuthentikApplicationRequest body); [Delete("/api/v3/core/applications/{slug}/")] Task DeleteApplicationAsync(string slug); // ── Groups ────────────────────────────────────────────────────────────── [Get("/api/v3/core/groups/")] Task> ListGroupsAsync([AliasAs("search")] string? search = null); [Post("/api/v3/core/groups/")] Task>> CreateGroupAsync( [Body] CreateAuthentikGroupRequest body); [Delete("/api/v3/core/groups/{id}/")] Task DeleteGroupAsync(string id); // ── Invitations ───────────────────────────────────────────────────────── [Post("/api/v3/stages/invitation/invitations/")] Task>> CreateInvitationAsync( [Body] CreateFlowRequest body); // ── Users ─────────────────────────────────────────────────────────────── [Post("/api/v3/core/users/")] Task>> CreateUserAsync( [Body] CreateAuthentikUserRequest body); // ── Health ────────────────────────────────────────────────────────────── [Get("/api/v3/-/health/ready/")] Task> CheckHealthAsync(); // ── Certificates ──────────────────────────────────────────────────────── [Get("/api/v3/crypto/certificatekeypairs/{kpId}/")] Task>> GetCertificateKeyPairAsync(string kpId); [Post("/api/v3/crypto/certificatekeypairs/")] Task>> ImportCertificateAsync( [Body] ImportCertRequest body); // ── SAML Sources ──────────────────────────────────────────────────────── [Post("/api/v3/sources/saml/")] Task>> CreateSamlSourceAsync( [Body] CreateSamlSourceRequest body); [Get("/api/v3/sources/saml/{slug}/metadata/")] Task> GetSamlSourceMetadataAsync(string slug); [Delete("/api/v3/sources/saml/{slug}/")] Task DeleteSamlSourceAsync(string slug); }