# 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: 1. **Configuration load** – `AuthorityPluginConfigurationLoader` resolves YAML manifests under `etc/authority.plugins/`. 2. **Assembly discovery** – the shared `PluginHost` scans `PluginBinaries/Authority` for `StellaOps.Authority.Plugin.*.dll` assemblies. 3. **Registrar execution** – each assembly is searched for `IAuthorityPluginRegistrar` implementations. Registrars bind options, register services, and optionally queue bootstrap tasks. 4. **Runtime** – the host resolves `IIdentityProviderPlugin` instances, 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_` 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.FromCapabilities` projects 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`): ```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 `SupportsClientProvisioning` is `true`, the plug-in must supply a working `IClientProvisioningStore`. **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. ## 4. Project Scaffold - Target **.NET 10 preview**, enable nullable, treat warnings as errors, and mark Authority plug-ins with `true`. - 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 from `StellaOps.Authority.Plugin.Standard`): ```xml net10.0 enable true true ``` (Add other references—e.g., MongoDB driver, shared auth libraries—according to your implementation.) ## 5. Implementing `IAuthorityPluginRegistrar` - Create a parameterless registrar class that returns your plug-in type name via `PluginType`. - Use `AuthorityPluginRegistrationContext` to: - Bind options (`AddOptions(pluginName).Bind(...)`). - Register singletons for stores/enrichers using manifest metadata. - Register any hosted bootstrap tasks (e.g., seed admin users). - Always validate configuration inside `PostConfigure` and throw meaningful `InvalidOperationException` to fail fast during startup. - Use the provided `ILoggerFactory` from DI; avoid static loggers or console writes. - Example skeleton: ```csharp internal sealed class MyPluginRegistrar : IAuthorityPluginRegistrar { public string PluginType => "my-custom"; public void Register(AuthorityPluginRegistrationContext context) { var name = context.Plugin.Manifest.Name; context.Services.AddOptions(name) .Bind(context.Plugin.Configuration) .PostConfigure(opts => opts.Validate(name)); context.Services.AddSingleton(sp => new MyIdentityProvider(context.Plugin, sp.GetRequiredService(), sp.GetRequiredService(), sp.GetRequiredService>())); } } ``` ## 6. Identity Provider Surface - Implement `IIdentityProviderPlugin` to expose: - `IUserCredentialStore` for password validation and user CRUD. - `IClaimsEnricher` to append roles/attributes onto issued principals. - Optional `IClientProvisioningStore` for machine-to-machine clients. - `AuthorityIdentityProviderCapabilities` to 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. - Health checks should probe backing stores (e.g., Mongo `ping`) and return `AuthorityPluginHealthResult` so `/ready` can surface issues. - When supporting additional factors (e.g., TOTP), implement `SupportsMfa` and 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____...`. - 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`; include `pluginName` and correlation IDs where available. - Activity/metric names should align with `AuthorityTelemetry` constants (`service.name=stellaops-authority`). - Expose additional diagnostics via structured logging rather than writing custom HTTP endpoints; the host will integrate these into `/health` and `/ready`. - Emit metrics with stable names (`auth.plugins..*`) 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 (`StandardUserCredentialStoreTests` is 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.Authority` with your plug-in under `PluginBinaries/Authority` and verify `/ready`. - Example verification snippet: ```csharp [Fact] public async Task VerifyPasswordAsync_ReturnsSuccess() { var store = CreateCredentialStore(); await store.UpsertUserAsync(new AuthorityUserRegistration("alice", "Pa55!", null, null, false, Array.Empty(), new Dictionary()), 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..dll` so the host’s search pattern picks it up. - Place the compiled DLL plus dependencies under `PluginBinaries/Authority` for 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/.yaml` samples 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 `bootstrap` capability 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.md` when that chapter is published to keep glossary terms consistent.