Add an External Authentication capability to Elsa 3 that lets administrators and deployers register Identity Provider Connections through either deployment configuration or optional database persistence. Elsa Server brokers external sign-in, resolves the authenticated External Identity to a tenant-scoped Elsa User, composes Elsa permissions, and issues Elsa credentials.
Elsa Studio will provide:
- A management experience for database-owned connections.
- A read-only view of configuration-owned connections.
- A unified login chooser for local Elsa credentials and enabled external connections.
- External Identity Link administration.
- Safe connection testing and interactive preview.
The first release supports OpenID Connect. The connection, adapter, policy, permission, and Studio contracts must allow future provider-specific OAuth adapters such as GitHub without changing the core data model or broker flow.
## Problem
Elsa currently supports local username/password authentication, Elsa-issued JWTs, and API keys. Elsa Studio also supports startup-configured direct OIDC authentication, but that implementation assumes one provider and does not support runtime provider administration.
Customers need to:
- Offer multiple external authentication choices.
- Add or change connections without restarting Elsa.
- Keep some connections deployment-controlled through configuration.
- Manage other connections from Elsa Studio.
- Preserve Elsa-specific authorization after external authentication.
- Extend the system with provider-specific adapters without adding database columns or rebuilding the management UI for every provider.
- Operate safely across tenants, Studio hosting models, and multi-node Elsa deployments.
Without a server-owned broker and a connection registry, Studio would need provider credentials and protocol-specific behavior, every client would implement account linking independently, and dynamically stored providers would not have a safe common execution model.
## Goals
- Make Elsa Server the single broker for external authentication.
- Support a merged registry of configuration-owned and database-owned connections.
- Let database-managed connection changes take effect without restart.
- Keep provider secrets outside connection records and read APIs.
- Full Elsa User and Role management UI. This feature supplies the backend Role-deletion guard and remediation contract only; a future Roles UI MAY integrate it, but External Authentication MUST NOT add a separate Settings page for Role deletion.
4.**Safe by default, flexible by explicit choice**: Exact discovery documents and restrictive policies are defaults; adapters may expose safe protocol-specific settings without making broker invariants editable.
5.**Broker protections are invariants**: Connection administrators cannot edit Elsa-owned callback derivation, correlation, S256 PKCE, one-time-code, or secret-redaction protections.
7.**No external permission authority**: External User Matchers may propose an existing user but never roles or permissions; Elsa alone expands Roles into permissions.
8.**Sources retain ownership**: Configuration-owned connections are deployer-owned and read-only in Studio.
9.**Operational state is explicit**: Enabled intent, validity, and observed health are separate concepts.
10.**Extension metadata is helpful, not authoritative**: Permission Descriptors improve authoring but do not define the validity of Elsa's open string permission vocabulary.
## Existing Product Surface
### Elsa Core
- Local login is exposed through the Elsa Identity module.
- Elsa Users carry local password hashes, roles, and tenant context.
- Elsa access-token issuance resolves role permissions and emits `permissions` claims.
- User and role CRUD APIs already exist.
- Configuration-based and store-based identity providers exist, but they are mutually exclusive rather than merged.
- JWT bearer and API-key authentication are supported; there is no existing external-login broker.
- Elsa Mediator provides `INotificationSender` for module notifications.
- ASP.NET Core health checks are already used by Elsa runtime modules.
### Elsa Studio
- Existing direct OIDC modules configure one provider at startup.
- Blazor Server and WebAssembly use different current authentication plumbing.
- The existing Security menu includes Users and Roles routes, but both pages are placeholders.
- Existing module, navigation, Refit client, CRUD page, dialog, and UI-hint patterns can support the new management experience.
1. An authorized administrator opens **Settings → SSO** at `/settings/sso-connections`.
2. Studio lists configuration-owned connections, Studio-owned connections, and explicit Studio Overrides for the currently connected Elsa server environment.
3. Record ID, logical Connection Key, source, override relationship, adapter type, enabled state, validity, latest on-demand test, preferred-login state, and revision are visible.
4. Configuration-owned connections are read-only until the administrator explicitly creates a complete Studio Override.
5. The administrator creates a disabled Studio-owned draft or an explicit full-shadow override and selects an installed Protocol Adapter.
7. Secret fields distinguish Managed Secrets from External Secrets. Managed values can be replaced or removed without reveal; External values remain deployment-owned and expose only configured/resolvable state.
8. The administrator runs a non-interactive connection test and may run an interactive Preview Sign-in.
9. Preview shows a redacted normalized identity, tenant, policy decision, and effective Elsa permissions without provisioning a user or opening a normal session.
10. The administrator enables the connection after structural validation and required Secret Binding resolution succeed.
11. The enabled connection becomes available across the Elsa cluster without restart.
9. If no link exists, the connection's effective Unlinked Identity Policy denies access, creates a user, or returns an explicitly configured target-user resolution. A target-user resolution must identify the user and authorization basis; it may not silently infer a link from email or user name.
10. The Unlinked Identity Policy rejects, creates a user with static authorized `defaultRoleIds`, or invokes one configured External User Matcher to propose an existing user. Claims required for matching remain ephemeral.
11. Elsa redirects Studio with a short-lived, single-use Elsa authorization code.
12. Studio exchanges the code using the PKCE verifier and establishes its Elsa session.
Local Elsa credentials use the same broker-completion contract: Studio submits credentials to an Elsa-owned local-login endpoint, Elsa validates them without exposing whether an account lacks a Local Credential, and successful authentication returns the same Authentication Client-, callback-, tenant-, and PKCE-bound completion code. Local authentication remains a Login Method, not an Identity Provider Connection.
The v1 OpenID Connect adapter accepts one exact HTTPS `discoveryUrl`, uses a deployment-derived provider callback, requires a confidential upstream client and authorization-code flow with S256 PKCE, and supports `client_secret_basic` or `client_secret_post`. The normal path trusts discovery. An authorized administrator may open **Advanced** and explicitly override discovery-derived issuer, endpoints, or signing keys when deployment policy permits; these settings require warning, confirmation, and notification. Callback derivation and all broker validation invariants remain immutable.
| Studio host | Authentication Client | Code exchange and callback owner | Browser/session outcome |
| --- | --- | --- | --- |
| Blazor Server | Confidential server client | The Studio server host owns the callback and exchanges the code with PKCE and its server-held client authentication | Provider and Elsa refresh credentials remain server-side; the browser receives only a secure, HTTP-only Studio session cookie |
| Blazor WebAssembly | Public browser client | The browser callback exchanges the code directly with PKCE; no client secret is issued or accepted | Elsa credentials are handled by the Studio token accessor, never placed in URLs, and use an explicit deployment storage policy whose safe default is in-memory only |
The broker must allow only exact registered callback and logout URIs. Public-client exchange must use an exact-origin CORS allowlist. A Studio host selects either **Direct OIDC** or **Brokered External Authentication** at deployment time; enabling both modes is a startup configuration error. Switching back to Direct OIDC is the supported rollback path.
All return targets—after local or external login, preview, and logout—must resolve to an allowlisted client-local path. User-controlled absolute or protocol-relative URLs are rejected.
1. An authorized administrator opens the dedicated External Identity Links page.
2. The administrator selects an Elsa User.
3. Studio lists safe link metadata and last successful sign-in information.
4. The administrator may pre-link a tenant, connection, issuer namespace, and subject for pre-provisioned-only admission.
5. The administrator may unlink an identity after explicit confirmation.
6. External tokens and unrestricted claims are never shown.
The user picker uses a tenant-scoped, permission-guarded, paginated lookup that returns only the minimum display identity needed to select an Elsa User. It never returns credential fields.
### Recovery
1. Deployment owners configure an independent Break-glass Authentication method when lockout protection is enabled.
2. Elsa rejects disabling the final valid sign-in path unless the caller uses an explicitly privileged, confirmed override.
3. A deployment owner can use the break-glass method to repair a failed provider without authenticating through that provider.
## Release Milestones
### Milestone 1: Configuration-first Broker Foundation
- **FR-011**: A Studio module MAY register a custom editor for an adapter type, but the generic renderer MUST remain sufficient to configure every adapter-declared field.
- **FR-013**: Configuration-owned connections MUST be visible but read-only unless an administrator explicitly creates a full Studio Override.
- **FR-014**: Studio-owned connections and Studio Overrides MUST support create, read, update, enable, disable, archive, restore, test, and Preview Sign-in operations.
- **FR-015**: Configuration MUST provide the baseline for an immutable host-wide Connection Key. An explicit active or disabled Studio Override for that key MUST completely shadow the baseline.
- **FR-016**: Overrides MUST be whole connection documents; no field-level merge across configuration and persistence is permitted.
- **FR-017**: A disabled override MUST continue shadowing and therefore disable the logical connection. Archiving or removing the override MUST deliberately reveal the configuration baseline; restoring it MUST resume the full shadow.
- **FR-018**: Every connection MUST have a stable record ID for management/transient broker correlation and an immutable logical Connection Key for durable links and long-lived sessions.
- **FR-019**: A Connection Key MUST be unique across logical connections and sources within the currently connected Elsa server environment.
- **FR-020**: Creating a new Studio-owned connection with a configuration-owned key MUST require the explicit override operation.
- **FR-021**: Tenant-specific SSO connection administration and tenant-local key reuse are deferred beyond v1.
- **FR-022**: Connection deletion MUST logically archive the connection, preserve identity links, and emit an audit-ready notification. Audit history is retained only when an external subscriber persists those notifications.
- **FR-027**: SSO connection administration MUST apply host-wide to the currently connected Elsa server environment. No persisted or editable Deployment Target/Server Environment field is part of v1.
- **FR-028**: Anonymous discovery MUST return that environment's host-wide methods only.
- **FR-029**: Provider tenant, issuer tenant, and current Studio tenant MUST NOT select a different SSO administration environment.
- **FR-030**: Connection record ID, target Elsa tenant, and material revision MUST be protected in broker state before external redirection.
- **FR-031**: External Identity Links MUST use the durable tuple `(target tenant, connectionKey, issuer namespace, stable subject)` in v1.
- **FR-036**: The initial implementation MUST integrate with Elsa Secrets for Managed Secrets and support replace/remove without reveal.
- **FR-037**: The initial implementation MUST provide an External Secret resolver for standard .NET configuration keys; those values and their lifecycle remain deployment-owned and read-only in Studio.
- **FR-039**: Studio MUST support replacement and removal only for Managed Secrets. External Secrets expose configured/resolvable state and reference metadata but no value-management actions.
- **FR-040**: Secrets, tokens, and unrestricted claims MUST NOT appear in responses, redirects, logs, health details, preview reports, or audit notifications.
### Connection Lifecycle
- **FR-041**: Disabled database connections MAY be saved as incomplete drafts.
- **FR-042**: Effective enablement MUST require adapter structural validation and resolution of required Secret Bindings.
- **FR-043**: Invalid configuration-owned connections MUST remain administratively visible but MUST NOT become effectively enabled.
- **FR-044**: Enabled state MUST represent administrative intent independently of observed health.
- **FR-045**: Provider health failures MUST NOT automatically disable or hide a structurally valid enabled connection.
- **FR-046**: Studio MUST expose on-demand connection testing with redacted results.
- **FR-047**: The module SHOULD offer an opt-in, separately tagged ASP.NET Core health check using the same adapter test contract.
- **FR-048**: V1 MUST NOT require continuous polling, health-history persistence, or a monitoring UI.
- **FR-049**: Authorized administrators MUST be able to Preview Sign-in against a disabled draft revision.
- **FR-050**: Preview MUST NOT create/link a user, issue a completion code, issue Elsa credentials, or open a normal session.
### OIDC Adapter
- **FR-051**: V1 MUST include an OpenID Connect Protocol Adapter.
- **FR-052**: The OIDC adapter MUST accept one exact absolute HTTPS `discoveryUrl`.
- **FR-053**: Elsa MUST derive the provider callback URI from deployment-owned external base-address configuration, the fixed adapter callback route, and immutable Connection Key so ownership changes do not alter it; Studio MUST NOT edit it.
- **FR-054**: Studio MUST expose discovery-derived issuer, authorization/token endpoints, and signing-key material as Advanced overrides when deployment policy permits and the caller holds the unsafe-provider-trust permission. Save MUST require explicit confirmation, persistent warning, and a redacted security notification.
- **FR-055**: The upstream OIDC client MUST be confidential and use authorization-code flow with S256 PKCE.
- **FR-056**: Connection administrators MUST NOT be able to weaken state, nonce, correlation, signature validation, audience, lifetime, deployment-derived callback, confidential-client, S256 PKCE, or secret-redaction invariants. Advanced values change trusted inputs, not whether validation runs.
- **FR-057**: The adapter MUST support `client_secret_basic` and `client_secret_post`, validate the full OIDC response, and declare upstream-logout capability.
- **FR-066**: Callback processing MUST reject a flow when the connection was disabled, archived, or materially revised after initiation.
- **FR-067**: Broker state, PKCE material, correlation state, and completion codes MUST work when initiation and completion occur on different Elsa nodes.
- **FR-068**: Material revision MUST cover adapter settings, Secret Binding identity/generation, Unlinked Identity Policy, matcher-policy settings, static `defaultRoleIds`, and override lifecycle. Display name, icon, and display order MAY use a presentation-only revision.
- **FR-069**: Initiation, callback, code exchange, and external-session refresh MUST verify authoritative enabled, archive, and effective material-revision state. Cache invalidation MAY improve freshness but MUST NOT be the security boundary.
### Elsa Users and Identity Links
- **FR-070**: Successful external authentication MUST resolve to an Elsa User before Elsa credentials are issued.
- **FR-071**: External Identity Links MUST be separate from Elsa Users.
- **FR-072**: One Elsa User MAY have multiple External Identity Links.
- **FR-073**: An Elsa User MAY exist without Local Credentials.
- **FR-074**: JIT provisioning MUST NOT generate placeholder passwords.
- **FR-075**: Elsa's User persistence model MUST be migrated so Local Credentials are absent or separate rather than represented by placeholder password hashes.
- **FR-076**: Local login for a credential-less user MUST fail with the same public result as other invalid credentials.
- **FR-077**: JIT provisioning MUST create a globally unique Elsa user name under the current identity-store contract, retry a detected name collision, and compensate the User created by a losing or failed link writer. Mutable provider profile attributes MUST NOT become identity keys. A future tenant-scoped user-name migration is outside this feature unless separately specified.
- **FR-078**: External Identity Links MUST be resolved by target tenant, immutable Connection Key, validated issuer namespace, and provider-stable subject.
- **FR-083**: Each connection MUST select its Unlinked Identity Policy; deployment configuration MUST define the default and allowed policy types.
- **FR-084**: Studio MUST show the effective per-connection policy and permit changes only when deployment policy and caller permissions allow.
- **FR-084A**: V1 MUST provide a generic matcher-based Unlinked Identity Policy that selects exactly one installed `IExternalUserMatcher` and declares a no-match action of `Reject` or `CreateUser`.
- **FR-084B**: A matcher MUST receive only its declared required normalized claims, those claims MUST remain ephemeral, and a single match MAY propose an existing Elsa User. No match follows the configured fallback; ambiguous results or errors reject.
- **FR-084C**: V1 MUST NOT ship an Elsa first-party verified-email matcher. Email/name matching remains unavailable unless a trusted deployment extension explicitly provides it.
### User Matching, Role Provisioning, and Permission Resolution
- **FR-088**: Elsa MUST remain authoritative for the `permissions` claims placed in Elsa-issued credentials and MUST derive them through Elsa Roles.
- **FR-089**: Each connection MAY define static `defaultRoleIds` used only when `CreateUser` creates a new Elsa User, including the matcher policy's create-user no-match fallback.
- **FR-090**: External User Matchers MUST NOT select, derive, or mutate roles or permissions.
- **FR-091**: Saving `defaultRoleIds` MUST authorize the actor to assign every selected Role using Elsa's role-delegation rules.
- **FR-092**: JIT provisioning MUST assign authorized static default roles in the same User-store write as credential-less User creation and MUST NOT return success until the unique external identity link is durable.
- **FR-093**: Matching an existing user MUST NOT change that user's roles.
- **FR-094**: Existing linked users MUST retain their Elsa-managed role assignments; ordinary sign-in MUST NOT mutate their roles.
- **FR-095**: V1 Studio MUST NOT expose claim-to-permission, group-to-permission, wildcard, pass-through, or claim-to-role mapping UI.
- **FR-096**: External User Matcher types MUST be trusted deployed extensions with stable IDs, versioned settings, descriptors, declared required claims, and deployment allowlists.
- **FR-097**: Missing/deleted default roles or unavailable matcher extensions MUST produce validation errors or warnings and MUST NOT broaden access.
- **FR-097A**: Deleting an Elsa Role MUST inspect every database- and configuration-owned `defaultRoleIds` reference in CreateUser and matcher no-match CreateUser policies, including disabled, archived, shadowed, and currently ineffective definitions, and MUST block ordinary deletion while any reference remains.
- **FR-097B**: A configuration-owned blocker MUST identify its sanitized configuration path and policy branch. Elsa MUST NOT rewrite deployment configuration automatically.
- **FR-097C**: The administration API MUST provide an authorized, prevalidated command that removes the Role from every editable database-owned JIT policy and deletes the Role only after no reference remains. The actor MUST be authorized to delete the Role and update every affected connection/policy.
- **FR-097D**: The remediation MUST be atomic when the Role and connection stores can share a transaction. Otherwise it MUST use the documented best-effort protocol: prevalidate the complete dependency set and revisions, remove editable references before attempting Role deletion, never delete the Role after an incomplete removal, return structured partial-progress diagnostics, and support safe idempotent retry.
- **FR-097E**: Preflight and remediation MUST warn when removal leaves a CreateUser path with no default Role and MUST require explicit confirmation. A changed dependency set or stale connection revision MUST prevent Role deletion.
- **FR-097F**: V1 MUST expose this backend guard/remediation contract without adding a Role-management or Role-deletion page to External Authentication Settings. A future Elsa Roles UI MAY consume the contract.
- **FR-098**: Role expansion into permission strings MUST remain the responsibility of Elsa Identity and installed modules.
- **FR-099**: The normalized identity used during linking/JIT MAY retain only the explicitly allowed redacted provenance; matcher-required claims and complete external claims MUST NOT be persisted.
- **FR-100**: Elsa token refreshes MUST re-evaluate the user's current Elsa Role assignments.
- **FR-101**: External refresh credentials MUST reference an External Authentication Session and MUST check its connection key, maximum age, and revocation state; existing local refresh credentials remain compatible.
- **FR-102**: A configurable maximum external session age MUST require fresh provider authentication.
- **FR-103**: Upstream tokens MUST be retained only when required for the configured Elsa-initiated upstream logout, protected server-side, and no longer than the external session.
- **FR-104**: Provider access and refresh tokens MUST otherwise be discarded after callback processing and optional user-info retrieval.
- **FR-106**: Login Methods MUST unify local Elsa credentials and external connections without modeling local login as an Identity Provider Connection.
- **FR-113**: The initial Studio module MUST support Blazor Server and Blazor WebAssembly.
- **FR-114**: Blazor Server MUST use a confidential Authentication Client; the Studio host MUST perform exchange, keep Elsa refresh credentials server-side, and establish a secure HTTP-only browser session.
- **FR-115**: Blazor WebAssembly MUST use a public Authentication Client with no client secret, mandatory PKCE, exact-origin CORS, and an explicit token-storage policy that defaults to in-memory storage.
- **FR-116**: Studio MUST distinguish the connection's Upstream Client Registration from the deployment-owned Elsa Authentication Client in labels, help text, validation, and prerequisites.
- **FR-119A**: `Elsa.Studio.Authentication.UI` MUST own the generic login/logout shell and contribution contracts; the External Authentication module MUST contribute behavior without owning the shell.
- **FR-119B**: Settings MUST be a Studio composition/navigation surface only. SSO connection administration belongs at one-level **Settings → SSO** (`/settings/sso-connections`); External Identity Links and External Authentication Sessions remain separate Security pages.
- **FR-125A**: V1 MUST support only Elsa-initiated sign-in and logout. Unsolicited IdP-initiated login and front-channel/back-channel provider-initiated logout are out of scope.
- **FR-126**: Connection operations MUST use dedicated Elsa permissions for read, create, update, archive/restore, test, policy management, unsafe security overrides, identity-link management, and session revocation.
- **FR-127**: Configuration-owned connections MUST remain immutable through runtime APIs regardless of caller permissions.
- **FR-128**: Elsa MUST support a configurable final-login-path lockout guard.
- **FR-129**: When the guard is active, disabling the final valid sign-in path MUST require a deployment-owned Break-glass Authentication method or an explicitly privileged confirmed override.
- **FR-130**: Break-glass Authentication MUST NOT appear in normal Login Method discovery.
- **FR-131**: The module MUST publish typed, immutable, redacted security notifications through `INotificationSender`.
- **FR-132**: Notifications MUST cover connection and policy changes, secret replacement/removal, enable/disable/archive/restore, tests, previews, link changes, session revocation, and sign-in outcomes.
- **FR-133**: Notifications SHOULD contain actor, Connection Key, Elsa User ID when known, timestamp, outcome, correlation ID, and a redacted change summary.
- **FR-134**: The module MUST NOT require or own an audit persistence store.
### Errors and Abuse Protection
- **FR-135**: Broker failures returned to clients MUST use a documented stable set of safe error categories plus a correlation ID.
- **FR-136**: Provider response details MUST remain in redacted server diagnostics and security notifications.
- **FR-137**: Public errors MUST not distinguish unknown users from missing links.
- **FR-138**: Anonymous discovery, initiation, callback, and code-exchange endpoints MUST integrate with ASP.NET Core rate limiting.
- **FR-139**: State and completion codes MUST have strict expiration and single-use semantics.
### Hosted Client, Preview, and Management Safety
- **FR-140**: Local credential authentication MUST complete through the same Authentication Client-, callback-, tenant-, and PKCE-bound code contract as external authentication.
- **FR-141**: Every user-controlled return target MUST be validated as an allowlisted client-local path; absolute, protocol-relative, and unregistered targets MUST be rejected.
- **FR-142**: A Studio host MUST select one active authentication mode. Direct OIDC and Brokered External Authentication may coexist as installed modules but MUST NOT both own login routes in one host.
- **FR-143**: Studio MUST show host-wide Login Methods only in v1 and MUST NOT offer an anonymous tenant picker.
- **FR-144**: Adapter outbound HTTP used for discovery, testing, preview, and callbacks MUST apply deployment egress policy, HTTPS-by-default, bounded time and response size, controlled redirects, DNS and resolved-address checks, and redacted exception handling.
- **FR-145**: Deployment policy MUST be able to deny private, loopback, link-local, reserved, or unapproved provider destinations and to route adapter traffic through an approved proxy.
- **FR-146**: Preview MUST use separate short-lived, one-time state and result records bound to administrator, connection record ID, draft revision, and preview callback.
- **FR-147**: Preview results MUST use an explicit field allowlist, be readable once by the initiating authorized administrator, and be discarded if that administrator's Studio session is lost or expires.
- **FR-148**: Management APIs and Studio routes MUST enforce operation permissions independently of menu visibility. The UI MUST accurately disable or hide unauthorized actions without treating that presentation as the security boundary.
- **FR-149**: Studio MUST explain configuration-owned, shadowed, archived, invalid, and stale-test states and show only the actions valid for the caller and current revision.
- **FR-150**: A last observed connection result MUST be labeled as an on-demand test with timestamp and tested revision; it MUST become stale after material change and MUST NOT be presented as continuous health.
- **FR-151**: Login Method buttons MUST be text-first, keyboard and screen-reader accessible, use trusted server-hosted assets with a safe fallback, and apply deterministic ordering. Display names and assets MUST be validated to reduce login-page spoofing.
- **FR-152**: External-authentication management MUST remain reachable through an independent local or Break-glass Authentication path when configured; it MUST NOT depend on the connection being repaired.
### Compatibility
- **FR-153**: Existing direct Studio OIDC modules MUST remain supported during Elsa 3 adoption.
- **FR-154**: Server-brokered External Authentication MUST be the recommended path for multiple and runtime-managed providers.
- **FR-155**: Migration documentation MUST map existing direct OIDC settings to one configuration-owned broker connection.
- **FR-156**: Migration MUST NOT silently move client secrets or change authentication mode.
- **FR-157**: Direct OIDC MAY be marked deprecated only after broker parity and migration tooling are available; it MUST remain supported throughout Elsa 3.x.
- **FR-158**: Removal of Direct OIDC MUST require a future major release, advance notice, and a tested rollback/migration guide.
| Elsa User | Owns Elsa authorization | Tenant context, optional Local Credential, roles and other Elsa-specific data |
| Unlinked Identity Policy Selection | Decides what to do with an unknown identity | Policy type, settings version, settings, inherited or connection override |
| External User Matcher Selection | Proposes an existing user for an unlinked identity | One matcher type, versioned settings, declared required ephemeral claims, no-match fallback |
Settings is a Studio UI composition and navigation surface only. It does not introduce server-side Settings entities or generic Settings persistence. `Elsa.Studio.Authentication.UI` owns the clean login/logout shell and accepts contributions from local, external, and future authentication modules.
The connection list should support filtering by source, adapter type, enabled state, validity, override/shadow state, and archived state. The currently connected server environment supplies the host-wide context; no target field is rendered.
Configuration-owned connections are inspect-only until the administrator explicitly chooses **Create Studio Override**. Every page and API independently enforces authorization; menu visibility is only an affordance. The connection editor labels provider-issued fields as **Upstream Client Registration**, shows the deployment-derived callback as read-only, and displays the eligible deployment-owned **Elsa Authentication Client** only as a redacted prerequisite.
- Require dedicated permissions and audit notifications for unsafe settings and privileged actions.
- Avoid account enumeration in public errors.
- Do not persist complete external claim sets.
- Keep remote assets off the anonymous login page.
- Constrain outbound provider traffic to the deployment's egress and destination policy.
- Apply bounded lifetimes to state, completion codes, access tokens, refresh ability, and external claim snapshots.
## Operational Requirements
- Database-managed changes must propagate across the cluster without restart.
- Security decisions at initiation, callback, exchange, and refresh must verify authoritative current state rather than depend only on cache propagation.
- Shared broker state must allow callbacks and code exchange on any node.
- Configuration-owned changes may require deployment/restart.
- Health failures must not auto-disable connections.
- Provider outages must not make Elsa unready or trigger restart loops by default.
- Connection changes must emit redacted diagnostics and security notifications with correlation IDs.
- Disabling or archiving must stop new authentication and refresh before existing short-lived access tokens expire.
## Acceptance Scenarios
### A. Configuration-owned OIDC connection
Given a deployment-defined enabled OIDC connection, when Studio discovers Login Methods, then the connection appears read-only and a user can complete brokered sign-in without provider credentials being exposed to Studio.
### B. Database-owned connection lifecycle
Given an installed OIDC adapter and writable persistence, when an authorized administrator creates a disabled draft, supplies settings and a Secret Binding, previews sign-in, and enables it, then the method becomes available without restarting any Elsa node.
Given a configuration connection, when an authorized administrator creates a Studio Override for its immutable key, then the complete override shadows configuration without field merging. Disabling the override keeps the logical connection disabled; archiving it reveals configuration; restoring it resumes the shadow.
Given a successful external identity with no link and an effective JIT policy, when the broker completes sign-in, then Elsa creates an Elsa User without local password material, assigns authorized default/matcher roles with that User write, publishes one durable link, compensates a failed publication, and issues Elsa credentials from Elsa role permissions only after both records exist.
Given an effective reject-unlinked policy, when an unlinked identity authenticates, then Elsa returns a safe denial. After an authorized administrator pre-links the identity, the same external sign-in succeeds.
Given the matcher-based policy, when its one `IExternalUserMatcher` returns one user, Elsa links that user without changing roles. No match rejects or creates a new user according to configuration; create-user assigns only authorized static `defaultRoleIds`. Ambiguous/error results reject, and matcher claims are not retained.
Given Studio is connected to an Elsa server environment, when SSO connections are administered or discovered, then they apply host-wide to that environment without a target entity or editable environment field.
Given a sign-in initiated on revision 4, when an administrator materially updates or disables the connection before callback, then callback rejects the flow with a safe retry error and does not complete against the new revision.
### I. Cluster callback
Given a sign-in initiated on node A, when the provider callback reaches node B, then shared protected state allows safe completion and the code remains single-use.
### J. Secret confidentiality
Given a configured client secret, when callers list, read, edit, test, preview, or audit the connection, then they can determine only whether the secret is configured and never receive its value.
### K. Unsafe provider-trust override
Given deployment policy permits unsafe overrides and the caller has the dedicated permission, when the caller confirms an override, then Elsa applies it, displays a persistent warning, and emits a redacted security notification without weakening Broker Security Invariants.
### L. Disabled or archived connection
Given an active external session, when its connection is disabled or archived, then new initiation, in-flight callback, and refresh fail while existing access tokens follow their configured short expiry.
### M. Upstream logout
Given an adapter supports upstream logout, when the connection mode is `UserChoice`, then Studio offers local logout and a separate provider logout action. When mode is `Always`, normal logout also initiates provider logout.
Given a preferred enabled connection, when Studio renders login, then that method is ordered and emphasized while the complete chooser remains visible and no automatic redirect occurs.
Given final-login-path protection is active and no verified recovery path or privileged override is present, when an administrator attempts to disable the last valid method, then Elsa rejects the operation.
### P. Compatibility
Given an existing Studio direct OIDC deployment, when the new module is introduced but not selected, then existing authentication continues unchanged.
### Q. Studio hosting profiles
Given a Blazor Server client, when brokered login completes, then the server exchanges the code and the browser receives only an HTTP-only Studio session. Given a Blazor WebAssembly public client, then no client secret is accepted, PKCE and exact-origin CORS are enforced, and no credential appears in a URL.
### R. Redirect safety
Given an attacker supplies an absolute or protocol-relative return target to login, chooser recovery, preview, or logout, when Elsa or Studio validates it, then the target is rejected rather than followed.
Given a connection administrator may not assign an Elsa Role, when they add it directly or make it reachable through a matcher, then Elsa rejects the change. No claim-permission mapping controls are present.
Given an external session, when mapped provider claims change upstream without a fresh external sign-in, then refresh retains the external snapshot but re-evaluates current Elsa-owned role grants. When the session is revoked, its connection is disabled, or its maximum age is exceeded, refresh fails without changing local-session refresh behavior.
### U. Preview isolation
Given an administrator previews a disabled draft, when the preview callback completes, then only the initiating active administrator can read the one-time allowlisted result; no link, user, normal code, Elsa credential, or normal session is created.
### V. Authentication-mode conflict
Given Direct OIDC and Brokered External Authentication are both selected for one Studio host, when the host starts, then startup fails with a configuration error. Restoring the Direct OIDC selection provides the documented rollback.
### W. Unknown tenant context
Given an anonymous Studio client has no trusted tenant context, when it discovers Login Methods, then only host-wide methods are returned and no tenant names or existence signals are exposed.
### X. Credential-less user
Given an external-only Elsa User has no Local Credential, when local login is attempted, then it fails like any invalid credential. External sign-in and tenant-scoped identity resolution continue to work.
Given an Elsa Role is referenced by any CreateUser or matcher no-match CreateUser `defaultRoleIds`, when ordinary Role deletion is requested, then deletion is blocked and all safe database/configuration reference diagnostics are returned. Configuration references include their sanitized configuration paths and remain untouched. When only editable database references remain and an authorized administrator confirms all empty-default-role and best-effort warnings, remediation removes the Role from every referenced policy and deletes the Role only after revalidation proves no dependency remains; any incomplete best-effort run leaves the Role intact and reports safe retry state.
- Role-lifecycle tests prove ordinary deletion is blocked by every persisted or configured JIT-policy reference, configuration paths are actionable but never mutated, and remediation cannot delete the Role after incomplete reference removal.