Skip to content

Releases: cirreum/Cirreum.Runtime.Authentication

Release v1.1.0

Choose a tag to compare

@hyspdrt hyspdrt released this 09 Jul 17:37

Cirreum.Runtime.Authentication 1.1.0 — Live auth-event delivery

Why this release exists

Until now, the auth-event contracts in Cirreum.Kernel (IAuthenticationEvent, IAuthenticationEventPublisher, the four framework event records) had handlers but no delivery path: an admin action like "revoke this API key" or "force sign-out" had no way to reach a running replica. Revocation was correct at startup (boot hydration + the per-request fail-closed consult) but a credential revoked while a replica was running stayed usable there until that replica restarted.

This release ships the missing live-delivery leg. Together with the already-released consumers — the ApiKey denylist handler, the grant-cache invalidator in Cirreum.Domain, and the connection terminator in Cirreum.Services.Server — publishing one event now takes effect immediately, on every replica.

What's new

The default publisher, registered automatically. AddAuthentication(...) now registers InProcessAuthenticationEventPublisher (TryAdd — your own registration wins). Dispatch is synchronous and ordered: consumer handlers first, each isolated (a throwing handler is logged and the rest still run), transport bridges last — so an event never ships with unapplied local effects. Failures surface to the caller as an AggregateException; handlers are idempotent, so republishing is the safe retry. A single-replica app is complete with zero wiring:

public sealed class RevokeApiKeyHandler(IAuthenticationEventPublisher events) {
    public async Task<Result> HandleAsync(RevokeApiKey command, CancellationToken ct) {
        // ...revoke in the store (the durable, authoritative act)...
        await events.PublishAsync(new CredentialRevoked(command.KeyId, command.Subject, DateTimeOffset.UtcNow) {
            CredentialType = "apikey",
            ExpiresAt = command.KeyExpiry,
        }, ct);
        return Result.Success();
    }
}

Cross-replica delivery: auth.AddEventCoordination(). Rides the Cirreum.Coordination broadcast primitive (ISignalBroadcaster) — Redis pub/sub in production, the safe in-process default otherwise:

builder.AddAuthentication(auth => auth
    .AddApiKey(...)
    .AddEventCoordination()                          // cross-replica auth-event delivery
    .ConfigureCoordination(c => c.UseRedis()));      // order-independent

Behind the verb: a versioned event registry (Kernel [MessageVersion] machinery — the four framework events plus any public app-defined IAuthenticationEvent), an outbound sender, and an inbound receiver that dispatches wire events to local handlers except the senders themselves — a publish-receive loop is structurally impossible, and an ambient inbound-dispatch scope additionally bars wire re-entry from handlers that publish. The subscription opens in the ISystemInitializer phase, before any boot hydrator, closing the startup race by construction.

Scoped by default. Coordination state — including this channel — is namespaced to {applicationName}:{environmentName} automatically (both here and via ConfigureCoordination in Cirreum.AuthenticationProvider 1.2.0); an explicit WithScope(...) always wins.

Delivery semantics

At-most-once, unbuffered — a replica disconnected from the backend misses events published in that window, permanently. This is deliberate: boot hydration + the per-request fail-closed consult remain the durable correctness path; this channel is the low-latency leg. Two operational caveats: whoever can publish on the coordination connection can forge auth events (isolate it via UseRedis("connectionKey") when its writer set is broader than the app), and on Azure Managed Redis pub/sub does not cross active geo-replication regions — run region-local backends when active-active.

Compatibility

Additive minor. Nothing activates unless you call AddEventCoordination(); without it, behavior is identical to 1.0.x plus the (previously missing) in-process publisher. StackExchange.Redis becomes an ambient transitive dependency via the new direct Cirreum.Coordination.Redis reference — consistent with the umbrella's reference-everything/opt-in-via-code pattern.

This release also folds in an internal consolidation with no public-API or wire change: the event registry now takes its inbound (identifier, version) → type resolution from the shared Kernel registry base (dropping a duplicate assembly scan it used to carry), and the two internal delivery types were renamed to the framework's sender/receiver vocabulary. Apps touch neither — IAuthenticationEventPublisher and IAuthenticationEventHandler<TEvent> are unchanged, and the published IAuthenticationEventTransportBridge marker keeps its name.

See also

  • Cirreum.Coordination 1.2.0 — ISignalBroadcaster, CoordinationScope
  • Cirreum.Services.Server 1.3.0 — connection registry + termination handler
  • Cirreum.Authentication.ApiKey 1.0.2 — denylist evict-on-expiry

Release v1.0.1

Choose a tag to compare

@hyspdrt hyspdrt released this 05 Jul 00:00

Full Changelog: v1.0.0...v1.0.1

Release v1.0.0

Choose a tag to compare

@hyspdrt hyspdrt released this 03 Jul 23:23

Cirreum.Runtime.Authentication 1.0.0 — One package, one AddAuthentication() call

Cirreum.Runtime.Authentication is the app-facing umbrella for the Authentication pillar. Install this one package, call AddAuthentication(...) once, and the whole track wires up — the framework-shipped schemes, a dynamic forward scheme that picks the right one per request, the audience-routed claims transformer that produces your Cirreum IApplicationUser, and a boot-time validator that fails fast on misconfiguration. Only the schemes you configure activate.

Strictly additive — initial release. A new package in the Cirreum 1.0 Foundation Reset; no predecessor. Targets .NET 10.0.


Why this release exists

Wiring authentication by hand means composing six scheme packages, a per-request dispatch chain, a claims transformer, and a startup validator — and getting the order and the fail-fast behavior right. The umbrella does it behind a single call, so an app turns on exactly the schemes it wants and nothing it doesn't.


What's new

builder.AddAuthentication(...) — the one entry point

var builder = DomainApplication.CreateBuilder(args);

builder.AddAuthentication(auth => auth
    .AddApiKey(options => { /* static clients and/or .AddResolver<T>() */ })
    .AddSignedRequest<MyClientResolver>()
    .AddSessionTicket());
    // Oidc / Entra / External activate automatically from their
    // Cirreum:Authentication:Providers:* configuration sections.

await using var app = builder.Build();
app.UseDefaultMiddleware();   // includes UseAuthentication() + UseAuthorization()

One call composes the schemes, selectors, handlers, the dynamic forward scheme, and the claims transformer, then runs the boot-time validator. It must run on a Cirreum host (DomainApplication.CreateBuilder) — the audience providers branch on Web API vs Web App — and it's idempotent, returning a CirreumAuthenticationBuilder you can reuse.

Two ways a scheme turns on

  • Configuration-declaredOidc, Entra, External activate from their Cirreum:Authentication:Providers:* sections; presence of the section is the opt-in (no code call).
  • Code-composedApiKey, SignedRequest, SessionTicket are enabled with a verb in the callback (AddApiKey, AddSignedRequest<T>, AddSessionTicket).

Every scheme's registration bails early when it isn't configured, so installing the package activates nothing you haven't asked for.

Per-request dispatch + fail-fast validation

AddAuthentication makes a dynamic forward scheme the default. Per request, a chain of selectors decides the handler: a conflict sentinel runs first and fails closed (401) when credentials for two different scheme categories are present; an audience selector routes Bearer JWTs to the matching configured provider; an anonymous fallback claims the rest so [AllowAnonymous] works. At startup, a Bearer-prefix validator ensures opaque-Bearer schemes (ApiKey, SessionTicket, …) declare unique token prefixes — failing fast on a collision rather than mis-routing at runtime.

CirreumAuthenticationBuilder

The builder the scheme packages extend — AddApiKey, AddSignedRequest<T>, AddSessionTicket, AddExternalTenantResolver<T>, AddApplicationUserResolver<T> — carrying the host IConfiguration so each verb can bind its own provider section.


How it pairs with the rest of the Authentication pillar

This umbrella transitively references all six Cirreum.Authentication.* scheme packages and the Cirreum.Runtime.AuthenticationProvider composition driver. Apps install only this package; the schemes and the driver flow in, and each scheme's own package documents its configuration and security model.


Compatibility

  • Additive. Initial release.
  • .NET 10.0. Requires a Cirreum host (DomainApplication.CreateBuilder).
  • Transitively references the six scheme packages (ApiKey, SignedRequest, SessionTicket, Oidc, Entra, External), Cirreum.Runtime.AuthenticationProvider, and the ASP.NET shared framework.

See also

  • CHANGELOG.md — condensed change list for 1.0.0.
  • README.md — full usage, scheme reference, and dispatch model.