11 KiB
Authority Plug-in Developer Guide
Status: Ready for Docs/DOC4 editorial review as of 2025-10-10. Content aligns with PLG6 acceptance criteria and references stable Authority primitives.
1. Overview
Authority plug-ins extend the StellaOps Authority service with custom identity providers, credential stores, and client-management logic. Unlike Feedser plug-ins (which ingest or export advisories), Authority plug-ins participate directly in authentication flows:
- Use cases: integrate corporate directories (LDAP/AD), delegate to external IDPs, enforce bespoke password/lockout policies, or add client provisioning automation.
- Constraints: plug-ins load only during service start (no hot-reload), must function without outbound internet access, and must emit deterministic results for identical configuration and input data.
- Ship targets: target the same .NET 10 preview as the host, honour offline-first requirements, and provide clear diagnostics so operators can triage issues from
/ready.
2. Architecture Snapshot
Authority hosts follow a deterministic plug-in lifecycle. The flow below can be rendered as a sequence diagram in the final authored documentation, but all touchpoints are described here for offline viewers:
- Configuration load –
AuthorityPluginConfigurationLoaderresolves YAML manifests underetc/authority.plugins/. - Assembly discovery – the shared
PluginHostscansPluginBinaries/AuthorityforStellaOps.Authority.Plugin.*.dllassemblies. - Registrar execution – each assembly is searched for
IAuthorityPluginRegistrarimplementations. Registrars bind options, register services, and optionally queue bootstrap tasks. - Runtime – the host resolves
IIdentityProviderPlugininstances, uses capability metadata to decide which OAuth grants to expose, and invokes health checks for readiness endpoints.
Data persistence primer: the standard Mongo-backed plugin stores users in collections named authority_users_<pluginName> and lockout metadata in embedded documents. Additional plugins must document their storage layout and provide deterministic collection naming to honour the Offline Kit replication process.
3. Capability Metadata
Capability flags let the host reason about what your plug-in supports:
- Declare capabilities in your descriptor using the string constants from
AuthorityPluginCapabilities(password,mfa,clientProvisioning,bootstrap). The configuration loader now validates these tokens and rejects unknown values at startup. AuthorityIdentityProviderCapabilities.FromCapabilitiesprojects those strings into strongly typed booleans (SupportsPassword, etc.). Authority Core will use these flags when wiring flows such as the password grant. Built-in plugins (e.g., Standard) will fail fast or force-enable required capabilities if the descriptor is misconfigured, so keep manifests accurate.- Typical configuration (
etc/authority.plugins/standard.yaml):plugins: descriptors: standard: assemblyName: "StellaOps.Authority.Plugin.Standard" capabilities: - password - bootstrap - Only declare a capability if the plug-in genuinely implements it. For example, if
SupportsClientProvisioningistrue, the plug-in must supply a workingIClientProvisioningStore.
Operational reminder: the Authority host surfaces capability summaries during startup (see AuthorityIdentityProviderRegistry log lines). Use those logs during smoke tests to ensure manifests align with expectations.
Configuration path normalisation: Manifest-relative paths (e.g., tokenSigning.keyDirectory: "../keys") are resolved against the YAML file location and environment variables are expanded before validation. Plug-ins should expect to receive an absolute, canonical path when options are injected.
4. Project Scaffold
- Target .NET 10 preview, enable nullable, treat warnings as errors, and mark Authority plug-ins with
<IsAuthorityPlugin>true</IsAuthorityPlugin>. - Minimum references:
StellaOps.Authority.Plugins.Abstractions(contracts & capability helpers)StellaOps.Plugin(hosting/DI helpers)StellaOps.Auth.*libraries as needed for shared token utilities (optional today).
- Example
.csproj(trimmed fromStellaOps.Authority.Plugin.Standard):(Add other references—e.g., MongoDB driver, shared auth libraries—according to your implementation.)<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> <IsAuthorityPlugin>true</IsAuthorityPlugin> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj" /> <ProjectReference Include="..\..\StellaOps.Plugin\StellaOps.Plugin.csproj" /> </ItemGroup> </Project>
5. Implementing IAuthorityPluginRegistrar
- Create a parameterless registrar class that returns your plug-in type name via
PluginType. - Use
AuthorityPluginRegistrationContextto:- Bind options (
AddOptions<T>(pluginName).Bind(...)). - Register singletons for stores/enrichers using manifest metadata.
- Register any hosted bootstrap tasks (e.g., seed admin users).
- Bind options (
- Always validate configuration inside
PostConfigureand throw meaningfulInvalidOperationExceptionto fail fast during startup. - Use the provided
ILoggerFactoryfrom DI; avoid static loggers or console writes. - Example skeleton:
internal sealed class MyPluginRegistrar : IAuthorityPluginRegistrar { public string PluginType => "my-custom"; public void Register(AuthorityPluginRegistrationContext context) { var name = context.Plugin.Manifest.Name; context.Services.AddOptions<MyPluginOptions>(name) .Bind(context.Plugin.Configuration) .PostConfigure(opts => opts.Validate(name)); context.Services.AddSingleton<IIdentityProviderPlugin>(sp => new MyIdentityProvider(context.Plugin, sp.GetRequiredService<MyCredentialStore>(), sp.GetRequiredService<MyClaimsEnricher>(), sp.GetRequiredService<ILogger<MyIdentityProvider>>())); } }
6. Identity Provider Surface
- Implement
IIdentityProviderPluginto expose:IUserCredentialStorefor password validation and user CRUD.IClaimsEnricherto append roles/attributes onto issued principals.- Optional
IClientProvisioningStorefor machine-to-machine clients. AuthorityIdentityProviderCapabilitiesto advertise supported flows.
- Password guidance:
- Prefer Argon2 (Security Guild upcoming recommendation); Standard plug-in currently ships PBKDF2 with easy swap via
IPasswordHasher. - Enforce password policies before hashing to avoid storing weak credentials.
- Prefer Argon2 (Security Guild upcoming recommendation); Standard plug-in currently ships PBKDF2 with easy swap via
- Health checks should probe backing stores (e.g., Mongo
ping) and returnAuthorityPluginHealthResultso/readycan surface issues. - When supporting additional factors (e.g., TOTP), implement
SupportsMfaand document the enrolment flow for resource servers.
7. Configuration & Secrets
- Authority looks for manifests under
etc/authority.plugins/. Each YAML file maps directly to a plug-in name. - Support environment overrides using
STELLAOPS_AUTHORITY_PLUGINS__DESCRIPTORS__<NAME>__.... - Never store raw secrets in git: allow operators to supply them via
.local.yaml, environment variables, or injected secret files. Document which keys are mandatory. - Validate configuration as soon as the registrar runs; use explicit error messages to guide operators. The Standard plug-in now enforces complete bootstrap credentials (username + password) and positive lockout windows via
StandardPluginOptions.Validate. - Cross-reference bootstrap workflows with
docs/ops/authority_bootstrap.md(to be published alongside CORE6) so operators can reuse the same payload formats for manual provisioning.
8. Logging, Metrics, and Diagnostics
- Always log via the injected
ILogger<T>; includepluginNameand correlation IDs where available. - Activity/metric names should align with
AuthorityTelemetryconstants (service.name=stellaops-authority). - Expose additional diagnostics via structured logging rather than writing custom HTTP endpoints; the host will integrate these into
/healthand/ready. - Emit metrics with stable names (
auth.plugins.<pluginName>.*) when introducing custom instrumentation; coordinate with the Observability guild to reserve prefixes.
9. Testing & Tooling
- Unit tests: use Mongo2Go (or similar) to exercise credential stores without hitting production infrastructure (
StandardUserCredentialStoreTestsis a template). - Determinism: fix timestamps to UTC and sort outputs consistently; avoid random GUIDs unless stable.
- Smoke tests: launch
dotnet run --project src/StellaOps.Authority/StellaOps.Authoritywith your plug-in underPluginBinaries/Authorityand verify/ready. - Example verification snippet:
[Fact] public async Task VerifyPasswordAsync_ReturnsSuccess() { var store = CreateCredentialStore(); await store.UpsertUserAsync(new AuthorityUserRegistration("alice", "Pa55!", null, null, false, Array.Empty<string>(), new Dictionary<string, string?>()), CancellationToken.None); var result = await store.VerifyPasswordAsync("alice", "Pa55!", CancellationToken.None); Assert.True(result.Succeeded); Assert.True(result.User?.Roles.Count == 0); }
10. Packaging & Delivery
- Output assembly should follow
StellaOps.Authority.Plugin.<Name>.dllso the host’s search pattern picks it up. - Place the compiled DLL plus dependencies under
PluginBinaries/Authorityfor offline deployments; include hashes/signatures in release notes (Security Guild guidance forthcoming). - Document any external prerequisites (e.g., CA cert bundle) in your plug-in README.
- Update
etc/authority.plugins/<plugin>.yamlsamples and include deterministic SHA256 hashes for optional bootstrap payloads when distributing Offline Kit artefacts.
11. Checklist & Handoff
- ✅ Capabilities declared and validated in automated tests.
- ✅ Bootstrap workflows documented (if
bootstrapcapability used) and repeatable. - ✅ Local smoke test + unit/integration suites green (
dotnet test). - ✅ Operational docs updated: configuration keys, secrets guidance, troubleshooting.
- Submit the developer guide update referencing PLG6/DOC4 and tag DevEx + Docs reviewers for sign-off.
Next documentation actions:
- Add rendered architectural diagram (PlantUML/mermaid) reflecting the lifecycle above once the Docs toolkit pipeline is ready.
- Reference the LDAP RFC (
docs/rfcs/authority-plugin-ldap.md) in the capability section once review completes. - Sync terminology with
docs/11_AUTHORITY.mdwhen that chapter is published to keep glossary terms consistent.