# Extension Points Map: Which Interface Owns Which Behavior

You have wired `AddOidcCore` and the server runs. Now the real questions start: clients must live in your database, signing keys in your vault, consent in your UI, and one [token-endpoint](https://www.abblix.com/en/docs/glossary-overview#token-endpoint) check has to follow your policy. Every one of those questions has the same shape (*which of the public interfaces owns this behavior, and how do I make my implementation stick?*), and this page maps the answers, seam by seam.

The [architecture guide](https://www.abblix.com/en/docs/understanding-the-architecture) explains why the server is built this way; the [Advanced DI guide](https://www.abblix.com/en/docs/advanced-dependency-injection) covers the underlying [DI](https://www.abblix.com/en/docs/glossary-overview#di) toolkit in depth. This page stays at the level of *to change X, replace Y*.

## Four rules cover every seam

**Singular seams honor host pre-registration: your registration wins if it comes first.** Register your implementation *before* `AddOidcCore` (or the feature's `Add*` call) and it takes the slot; the library's default never enters the container. The pattern is always the same:

```csharp
builder.Services.AddSingleton<IClientInfoProvider, DbClientInfoProvider>();
builder.Services.AddOidcCore(options => { /* ... */ });
```

Match the default's lifetime: nearly every seam resolves as a singleton, so a database-backed implementation takes `IServiceScopeFactory` and opens a scope per call rather than injecting a scoped `DbContext` directly.

**Pipeline families fold into composites: add members before composition, or edit the pipeline afterwards.** Multi-member families (request-pipeline validators, client authenticators, logout notifiers, request fetchers, grant handlers) are collapsed into one composite at composition time: during `AddOidcCore` for the always-on families, or inside the opt-in call itself (`AddDynamicClientRegistration`, `AddBackChannelAuthentication`, `AddDeviceAuthorization`) for the families those bring in. A member registered *after* that point does not extend the pipeline. It **replaces** it: singular resolution returns only your implementation, every built-in step silently stops running, and nothing detects that at startup or runtime. So either register the member before the composing call, or (from 2.4) take the pipeline apart with the [`Decompose<T>()` cursor](https://www.abblix.com/en/docs/advanced-dependency-injection#taking-the-pipeline-apart): `AddFirst`, `AddAfter<TExisting>`, `Remove<TExisting>`, `Replace<TExisting>` edit one step in one line. Grant handlers are the one family that instead fails loud with a clear exception when registered too late; for them, register-before is the only path. And not every `*Validator` is a family member: keyed validators such as `IAuthorizationDetailValidator` follow the keyed rule below.

**Keyed seams dispatch on a wire discriminator: register under the key the protocol sends.** Where the incoming message carries a discriminator (`alg`, `subject_token_type`, `authorization_details.type`, a [JWS](https://www.abblix.com/en/docs/glossary-overview#jws) `crit` name), implementations are registered as keyed services under that exact string, and unknown keys are rejected at the protocol level. Keyed registrations follow the same first-wins rule as singular ones: register your implementation under a built-in key before the library's `Add*` call and it replaces the shipped default for that key.

**Decorator seams wrap after registration: the default must already exist to be wrapped.** Where the table says *decorate*, call `services.Decorate<TService, YourDecorator>()` **after** `AddOidcCore`: it wraps whatever is currently registered. Registering your own implementation *before* instead replaces the seam and silently drops the built-in decorators already stacked on it (the authorize-request processor, for example, ships wrapped in [PAR](https://www.abblix.com/en/docs/glossary-overview#par) and session-management decorators). The [Advanced DI guide](https://www.abblix.com/en/docs/advanced-dependency-injection) covers `Decorate` in detail.

## The seams you must supply

Most seams have working defaults. Two do not:

- **`IUserInfoProvider`**: the bridge to your user store. The library ships no default: connecting your user store (ASP.NET Identity, a custom table, an external directory) is the one integration every host writes. The [Getting Started guide](https://www.abblix.com/en/docs/getting-started-guide) walks through it.
- **`IUserDeviceAuthenticationHandler`**: only if you enable [CIBA](https://www.abblix.com/en/docs/glossary-overview#ciba). The shipped stub throws by design: how your users approve a backchannel login on their device (push notification, authenticator app) is inherently yours to decide.

And one near-mandatory seam: **`IAuthSessionService`** has a real default only when your host runs cookie authentication: the shipped adapter binds to the host's cookie authentication scheme. Hosts with a different session model implement this seam directly.

## Clients and registration

| You need | Seam | Ships with | Notes |
|---|---|---|---|
| Clients in your database or config service | `IClientInfoProvider` | In-memory store over `OidcOptions.Clients` | About a hundred lines for a durable store; budget for caching. The provider sits on every request's hot path |
| Persist clients registered through DCR | `IClientInfoManager` | Same in-memory store | Pairs with the provider above |
| Custom client [JWKS](https://www.abblix.com/en/docs/glossary-overview#jwks) resolution or caching | `IClientKeysProvider` | Resolves keys from the client's registered JWKS or JWKS URI | |
| Your own dynamic-registration policy (allowed hosts, quotas, naming) | `IClientRegistrationContextValidator` | About twenty built-in steps | Composed family |
| Gate open registration with initial access tokens | `IInitialAccessTokenService` | Built-in issuance and validation | |
| client_id / client_secret format for DCR | `IClientIdGenerator`, `IClientSecretGenerator` | Secure random defaults | |

## Users, claims and consent

| You need | Seam | Ships with | Notes |
|---|---|---|---|
| Your user store behind the server | `IUserInfoProvider` | **Nothing. You supply it** | Mandatory for every deployment |
| Full control over claims assembly | `IUserClaimsProvider` | Orchestrates scopes, requested claims, subject conversion | |
| Custom scope-to-claims mapping | `IScopeClaimsProvider` | Standard OIDC scope mapping | |
| Dynamic or database-backed scope registry | `IScopeManager` | In-memory over `OidcOptions.Scopes` | |
| API resource registry (RFC 8707) | `IResourceManager` | In-memory over `OidcOptions.Resources` | |
| Persistent consent and a consent UI | `IUserConsentsProvider` | Auto-granting default | Replace to honor OIDC consent semantics; pending consents surface as a `ConsentRequired` outcome your host UI handles; `prompt=consent` handled by a decorator |
| Pairwise subject identifiers under your salt policy | `ISubjectTypeConverter` + `AddPairwiseSubjectIdentifiers(...)` | HMAC-SHA256 per OIDC Core §8.1 | |
| Custom session tracking (server-side sessions, [SSO](https://www.abblix.com/en/docs/glossary-overview#sso) policy) | `IAuthSessionService` | Adapter over the host's cookie authentication | See [The seams you must supply](#the-seams-you-must-supply) |

## Tokens and grants

| You need | Seam | Ships with | Notes |
|---|---|---|---|
| Extra [access-token](https://www.abblix.com/en/docs/glossary-overview#access-token) claims, custom token shaping | `IAccessTokenService` | Signed JWT per RFC 9068 | |
| [ID-token](https://www.abblix.com/en/docs/glossary-overview#id-token-identity-token) claim shaping | `IIdentityTokenService` | OIDC Core claims, nonce, hash-binding claims | |
| [Refresh-token](https://www.abblix.com/en/docs/glossary-overview#refresh-token) rotation and persistence policy | `IRefreshTokenService` | Rotation, absolute and sliding expiration | |
| A custom [grant type](https://www.abblix.com/en/docs/glossary-overview#grant-type) | `IAuthorizationGrantHandler` via `AddAuthorizationGrant<T>()` | Code, refresh, client credentials, JWT bearer, token exchange; password / device / CIBA opt-in | Composed family; registers dispatch and discovery in one call; must precede `AddOidcCore`, fails loud otherwise |
| Custom token-endpoint validation step | `ITokenContextValidator` | Five built-in steps in load-bearing order | Composed family; worked example in the [Advanced DI guide](https://www.abblix.com/en/docs/advanced-dependency-injection#taking-the-pipeline-apart) |
| Token and code lifetimes per client | Not a seam: `ClientInfo` properties | Per-client expirations with sensible defaults | Configuration, not code; for dynamic policy, shape tokens in `IAccessTokenService` |
| Audit events / SIEM hooks | Not a seam: no event bus ships | Structured log events with stable event ids | Decorate the service that owns the action, or consume the structured logs |
| Token exchange for new token types | `ISubjectTokenResolver` | JWT and refresh-token resolvers | Keyed by `subject_token_type` |
| Trusted issuer registry for the JWT bearer grant | `IJwtBearerIssuerProvider` | Config-driven registry with JWKS resolution | |
| Revocation-state storage | `ITokenRegistry` | Distributed-cache-backed status registry | |
| Replay-protection backend for assertions and proofs | `IJwtReplayCache` | Distributed-cache implementation | Use a shared cache ([Redis](https://www.abblix.com/en/docs/glossary-overview#redis)) across instances |

## Keys and cryptography

| You need | Seam | Ships with | Notes |
|---|---|---|---|
| Key rotation, vault- or database-held signing keys | `IAuthServiceKeysProvider` | Keys from `OidcOptions` certificates | [Signing-key persistence guide](https://www.abblix.com/en/docs/signing-key-persistence); the provider returns key material to the signing pipeline. Non-exportable [HSM](https://www.abblix.com/en/docs/glossary-overview#hsm) keys need the keyed signer seam below instead |
| Support a JWS `crit` header extension | `ICriticalHeaderHandler` via `AddCriticalHeaderHandler(name)` | Unknown `crit` rejected per RFC 7515 | Keyed by header name |
| Swap or add a signing or key-management algorithm (HSM-backed RS256, an algorithm the library does not ship) | `IDataSigner<TKey>`, `IKeyEncryptor<TKey>` | RSA, ECDSA, HMAC signing; RSA, AES-GCM key wrap, direct key agreement | Keyed by JWS / [JWE](https://www.abblix.com/en/docs/glossary-overview#jwe) `alg`; each implementation self-declares its `Algorithm`, so registered algorithms appear in discovery automatically |
| Custom hashing for stored secrets | `IHashService` | SHA-based hashing | |

## Sessions and logout

| You need | Seam | Ships with | Notes |
|---|---|---|---|
| Custom `session_state` computation or session store | `ISessionManagementService` | OIDC Session Management implementation | |
| Logout propagation beyond front/back-channel (message bus, custom protocols) | `ILogoutNotifier` | Front-channel and back-channel notifiers | Composed family |
| Custom delivery or retry of [back-channel logout](https://www.abblix.com/en/docs/glossary-overview#back-channel-logout) tokens | `ILogoutTokenSender` | HTTP delivery through SSRF-validated client | |
| Your own end-session request policy | `IEndSessionContextValidator` | Four built-in validation steps | Composed family |

## Storage and operational state

| You need | Seam | Ships with | Notes |
|---|---|---|---|
| Operational state in your cache or database (codes, PAR, device and CIBA requests) | `IEntityStorage` | `IDistributedCache`-backed storage | Must keep get-and-remove atomic. Single-use credentials depend on it |
| Key prefixes and namespacing in a shared cache | `IEntityStorageKeyFactory` | Standardized key layout | |
| Storage wire format (compatibility, migration) | `IBinarySerializer` | Protobuf with JSON fallback | |
| Custom authorization-code persistence | `IAuthorizationCodeService` | One-time codes over entity storage | |
| PAR request storage | `IAuthorizationRequestStorage` | Entity-storage-backed | |
| [PKCE](https://www.abblix.com/en/docs/glossary-overview#pkce) / nonce reuse-detection backend | `IAuthorizationValueReuseDetector` | Off until an interval is configured in `OidcOptions` | RFC 9700 hardening |

## Authorization pipeline and flows

| You need | Seam | Ships with | Notes |
|---|---|---|---|
| A custom [client-authentication](https://www.abblix.com/en/docs/glossary-overview#client-authentication) method | `IClientAuthenticator` | `none`, `client_secret_post`/`client_secret_basic`, `client_secret_jwt`/`private_key_jwt`, [mTLS](https://www.abblix.com/en/docs/glossary-overview#mutual-tls-client-authentication) variants | Composed family; each authenticator self-selects by credential shape |
| Custom authorize-request policy step | `IAuthorizationContextValidator` | Eleven built-in steps in load-bearing order | Composed family |
| A new request-object transport | `IAuthorizationRequestFetcher` | PAR, `request_uri`, inline request object | Composed family |
| A custom response type, or enabling implicit / none | `IAuthorizationResponseBuilder` via `AddAuthorizationResponseProcessor<T>()` | Code by default; `EnableImplicitFlow()`, `EnableNoneFlow()` opt-ins | A response type absent from DI does not exist: default-off semantics |
| Cross-cutting behavior at authorize time | `IAuthorizationRequestProcessor` (decorate) | Core processor, already decorated for PAR and session management | Decorator seam: see the fourth rule above |
| RAR: your `authorization_details` types | `IAuthorizationDetailValidator` via `AddAuthorizationDetailValidator(type)` | None: with no validators, RAR requests are rejected | Keyed by `type`; advertised in discovery automatically |
| DPoP proof-validation policy | `IProofValidator` | RFC 9449 validation | |
| [SSRF](https://www.abblix.com/en/docs/glossary-overview#ssrf) policy for outbound fetches (request_uri, JWKS, notifications) | `ISecureHttpFetcher`, `ISecureUriValidator` | SSRF-validating client, DNS-free URI checks | |
| CIBA user approval on the device | `IUserDeviceAuthenticationHandler` | **Stub that throws. You supply it** | Mandatory for CIBA |
| CIBA acceptance policy | `IBackChannelAuthenticationContextValidator` | Nine built-in steps | Composed family |
| Device-flow user-code format and verification UX | `IUserCodeGenerator`, `IUserCodeVerificationService` | Configurable alphabet; verification service your `/device` page calls | |

## Discovery, metadata and transport

| You need | Seam | Ships with | Notes |
|---|---|---|---|
| Per-tenant or multi-host issuer | `IIssuerProvider` | Configured issuer, or derived from the request when unset | Issuer string only: derive the tenant from ambient request context; client, key and scope providers are not tenant-aware and need the same tenant resolution |
| Dynamic or per-tenant [ACR](https://www.abblix.com/en/docs/glossary-overview#acr) advertisement (MFA levels) | `IAcrMetadataProvider` | Reads `OidcOptions.Discovery.AcrValuesSupported` | A static set needs no seam: configure the option |
| Control advertised scopes and claims | `IScopesAndClaimsProvider` | Aggregated from registered scopes | |
| The core's view of the incoming request (proxies, rewriting) | `IRequestInfoProvider` | Per-adapter HTTP implementation | Pairs with issuer strategy |
| Custom HTTP shaping of any endpoint response | `I*ResponseFormatter`, one per endpoint (e.g. `ITokenResponseFormatter`) | Defaults per endpoint in both adapters | Decorate or replace per endpoint: see the fourth rule above |
| Endpoint paths | `OidcRouteOptions` (Minimal API) / tokenized route templates from configuration (MVC) | `/connect/*`, `/.well-known/*` fallbacks | [Dynamic routes guide](https://www.abblix.com/en/docs/tokenized-routing-dotnet) |

## When the map is not enough

The seams above are the curated set, not the full inventory. The library's public surface is larger, and it is one file away: every default registration lives in the `ServiceCollectionExtensions` of its feature area in [the source](https://github.com/Abblix/Oidc.Server). If you still find no seam for the behavior you need, [open an issue](https://github.com/Abblix/Oidc.Server/issues): the extension point you need may already exist under a name this page omits, or it will become a candidate for the next release.
