14 KiB
Studio Contract: External Authentication
Packages
src/modules/
├── Elsa.Studio.Settings/
├── Elsa.Studio.Authentication.UI/
├── Elsa.Studio.ExternalAuthentication/
├── Elsa.Studio.ExternalAuthentication.BlazorServer/
└── Elsa.Studio.ExternalAuthentication.BlazorWasm/
Shared Module
Elsa.Studio.Settings owns UI composition/navigation contracts only. It does not define a server Settings entity, generic Settings persistence, or authorization semantics.
Elsa.Studio.Authentication.UI owns:
- The generic
/loginand/logoutshell. - Login Method chooser layout, accessibility, safe return paths, and error presentation.
- Authentication UI contribution contracts for local, brokered external, and future methods.
- Generic authentication navigation without provider-specific business logic.
Elsa.Studio.ExternalAuthentication contributes:
- Refit clients and API DTOs.
- Broker-backed Login Method provider and local/external contributions to the Authentication.UI shell.
- PKCE request model and external-authentication-specific state.
- Identity Provider Connection list/editor/test/preview.
- External Identity Link page and bounded user picker.
- Descriptor-driven field renderer and optional custom-editor registry.
- Settings navigation contribution for Connections and Security contributions for Links/Sessions.
- Common broker token/provider abstractions.
Registration:
services.AddExternalAuthenticationModule(backendApiConfig);
The module registers management only when the remote feature is available. It never replaces the Authentication.UI shell; the selected brokered host package activates its contributions.
UI Contribution Contracts
public interface ILoginMethodCatalog;
public interface ILoginMethodComponentProvider;
public interface ILoginMethodIconProvider;
public interface ISettingsNavigationContributor
{
ValueTask ContributeAsync(
SettingsNavigationBuilder builder,
CancellationToken cancellationToken = default);
}
public interface ISecurityMenuContributor
{
ValueTask ContributeAsync(
SecurityMenuBuilder builder,
CancellationToken cancellationToken = default);
}
ILoginMethodCatalog supplies ordered method metadata, ILoginMethodComponentProvider resolves method-specific UI, and ILoginMethodIconProvider resolves trusted local icons. Settings/Security contributors add navigation metadata only. None replace the shell, create duplicate parents, bypass route/API authorization, or persist arbitrary Settings data.
Server Host Package
Elsa.Studio.ExternalAuthentication.BlazorServer owns:
ElsaStudio.ExternalAuthentication.Cookiescheme.- Protected server-side broker transaction state.
/authentication/external/callback./authentication/external/logout-callback.- Back-channel code exchange and refresh.
- Server token provider for API clients and SignalR.
- Unauthorized component that redirects to
/login. - Cookie/session expiration and logout.
Registration:
services.AddExternalAuthenticationBroker(options =>
configuration.GetSection("Authentication:ExternalAuthentication").Bind(options));
Cookie requirements:
- Name:
ElsaStudio.ExternalAuthentication. HttpOnly = true.Secure = Always.SameSite = Lax.- No Elsa or provider token readable by browser code.
- Antiforgery required on state-changing BFF endpoints.
- Session lifetime does not exceed the external session.
The callback performs client-state and PKCE transaction validation before sending the Elsa completion code back-channel.
WebAssembly Host Package
Elsa.Studio.ExternalAuthentication.BlazorWasm owns:
- Browser-crypto PKCE verifier/challenge generation.
- Pre-exchange transaction state in
sessionStorage, deleted after callback. /authentication/external/callback./authentication/external/logout-callback.- Direct public-client code exchange and refresh.
- In-memory token provider by default.
- API/SignalR authorization integration.
- Unauthorized component that redirects to
/login.
Registration uses the same method name with host-specific overload/assembly:
services.AddExternalAuthenticationBroker(options =>
configuration.GetSection("Authentication:ExternalAuthentication").Bind(options));
No client secret is accepted. Persistent post-exchange browser token storage is an explicit deployment opt-in and displays a startup/security warning.
Authentication Provider Selection
The host reads Authentication:Provider:
| Value | Existing/new behavior | Broker chooser |
|---|---|---|
ElsaIdentity |
Existing Elsa username/password Studio authentication | Off |
OpenIdConnect |
Existing direct single-provider OpenID Connect | Off |
ElsaLogin |
Existing legacy login module | Off |
ExternalAuthentication |
New Elsa broker client | On |
Server retains its existing default (ElsaIdentity); WebAssembly retains its existing default (OpenIdConnect). Broker mode is explicit opt-in.
Startup fails when:
Authentication:Provider = ExternalAuthenticationbut broker client ID/callback is missing.- Broker mode attempts to register a public client on Server or confidential secret on WebAssembly.
- Direct OpenID Connect handlers are also enabled in broker mode.
- Callback, logout callback, or browser origin does not match the server-side Authentication Client registration.
Direct settings remain untouched for rollback.
Login Chooser Contribution
Route: /login
Flow:
- Resolve the validated client-local
returnPath; invalid values become/. - Fetch anonymous Login Methods with
ILoginMethodsApi. - Render all available local and external methods in deterministic order.
- Order and emphasize the preferred method without automatic redirect.
- Generate PKCE before initiating either local or external broker flow.
- Preserve only opaque client transaction state and local return path.
Presentation:
- Reuse
BasicLayout, branding, and localization. - Render a visible method name even if an icon fails.
- Resolve only trusted icon IDs through a local asset registry.
- Full keyboard operation, visible focus, accessible names, and status/error announcements.
- Do not render provider HTML, remote images, authority, or client identifiers.
Shared Refit Clients
public interface ILoginMethodsApi;
public interface IExternalAuthenticationBrokerApi;
public interface IIdentityProviderConnectionsApi;
public interface IExternalIdentityLinksApi;
public interface IExternalAuthenticationSessionsApi;
ILoginMethodsApiuses the anonymous backend API provider.- Broker exchange uses a dedicated client without an existing bearer handler.
- Management, link, and session clients use the authenticated backend API provider.
- DTOs mirror rest-api.md and do not define alternate Studio-only payloads.
Management Information Architecture
Settings
└── SSO
Security
├── External Identity Links
└── External Authentication Sessions
Routes:
/settings/sso-connections/settings/sso-connections/new/settings/sso-connections/{connectionId}/security/external-identity-links/security/external-authentication-sessions
Legacy connection-route aliases may remain for bookmarked links, but generated navigation and documentation use /settings/sso-connections.
Elsa.Studio.Settings owns Settings navigation and ISettingsNavigationContributor; External Authentication contributes one-level SSO. Elsa.Studio.Security owns Security and ISecurityMenuContributor; External Authentication contributes Links and capability-gated Sessions. Neither menu grants authorization.
Connection List
The list is server-paged and filters by:
- Search.
- Source.
- Adapter type.
- Enabled intent.
- Validity.
- Override/shadow state.
- Archive state.
Columns show display name/key, record ID, source/override relationship, adapter, enabled intent, validity, latest on-demand test with timestamp/revision/staleness, preferred state, and revision.
Behavior:
- Configuration-owned rows are inspect-only and offer Create Studio Override when authorized.
- Overrides visibly identify the configuration baseline and state that no fields are merged.
- Disabled overrides keep shadowing; archived overrides reveal configuration; restore resumes shadowing.
- Archived rows permit restore only when authorized.
- Caller permissions control action visibility, but every API remains authoritative.
Connection Editor
Sections:
- Identity: immutable Connection Key, display name, icon, order, preferred. The connected server environment is implicit and no target field is shown.
- Provider adapter: installed adapter and descriptor-driven fields.
- Upstream Client Registration: exact
discoveryUrl, confidential client ID,client_secret_basic/client_secret_post, Managed/External Secret Binding, read-only deployment-derived callback, and scopes. - Advanced Provider Trust: permission/deployment-gated overrides for discovery-derived issuer, authorization/token endpoints, and signing keys. The section is collapsed by default, labels values unsafe, requires confirmation on save, and keeps a persistent warning while any override is active.
- Claim projection: allowed/redacted claims and size limits.
- Admission and create-user roles: per-connection policy; one External User Matcher and Reject/CreateUser no-match action for matcher policy; static
defaultRoleIdsonly for CreateUser; role-assignment warnings. - Authorization explanation: matchers propose users, never roles; Elsa Roles produce permissions and no claim-role/permission mapping controls are rendered in v1.
- Logout: capability-driven mode.
- Status: enabled intent, validity, source/shadowing, latest test, revision.
Advanced trust controls never expose switches for callback derivation, confidential-client mode, S256 PKCE, state/correlation/nonce, signature validation, audience/lifetime validation, one-time codes, or secret redaction.
Secret fields show only configured/resolvable state. Managed Secrets have replace/remove actions; External Secrets show deployment-owned reference/state and no value-lifecycle action. The editor never binds a returned secret value.
Save uses If-Match. On 412, Studio preserves unsaved values, loads the current safe model, and offers reload or manual reapply—never silent overwrite.
Test and Preview
Test
- Requires test permission.
- Shows progress, then redacted category, summary, warnings, duration, correlation ID, tested revision, and timestamp.
- Never labels the result continuous health.
- Immediately marks a result stale after a material edit.
Preview Sign-in
- Requires preview permission and explicit confirmation that no normal session/user/link will be created.
- Opens the Elsa-owned preview navigation URL.
- On return, reads the result once from the initiating active administrator session.
- Displays masked identity, allowlisted preview claims, proposed match/no-match policy decision, safe proposed user/link action, static create-user roles when applicable, and warnings. Matcher-required claims are not retained in the result.
- If the administrator session is gone, display an expired result and do not retry/read it through anonymous state.
External Identity Links
The dedicated page:
- Uses the tenant-scoped paginated user option API.
- Filters links by user and connection.
- Displays connection, issuer, masked subject hint, created time, and last sign-in.
- Creates prelinks from explicit issuer and subject input.
- Requires confirmation before unlink.
- Never displays provider tokens, raw stored subject, full claims, credential fields, roles, or permission lists.
The user-link panel is a reusable component for a future user detail page.
Session Administration
When the server advertises session administration:
- List safe session metadata by user/connection/status.
- Revoke with confirmation and reason.
- Never display access/refresh tokens, hashes, raw subject, or claim snapshot.
Hide the section when the remote capability is absent.
UI Authorization Matrix
| Capability | UI behavior |
|---|---|
| Read only | Show list/detail; no mutation buttons |
| Create | Show New |
| Update | Edit database rows; enable/disable; secret binding actions |
| Archive | Archive/restore |
| Test | Test button |
| Preview | Preview button |
| Policy manage | Policy/grant editors |
| Unsafe provider trust | Unsafe controls with warning/confirmation |
| Link manage | Link page and mutations |
| Session read/revoke | Session section and revoke |
Route authorization and API authorization are both required. Menu visibility alone grants nothing.
Role Lifecycle UI Boundary
External Authentication contributes the backend Role-deletion dependency and remediation contract but no Role-management page or additional Settings route. A future Elsa Roles UI MAY call the deletion-impact and remediation APIs, display configuration paths and last-default-role warnings, and collect the required confirmations. Until such a UI exists, the contract remains available to authorized API clients.
Studio Verification
- Unit tests for descriptor form mapping, safe secret state, menu contribution, authorization affordances, preferred ordering without redirect, icon fallback, return-path validation, ETag recovery, and stale test labels.
- Server integration tests for protected transaction state, callback, back-channel exchange, cookie flags, server-only tokens, refresh, logout, API/SignalR token attachment, and conflicting startup mode.
- Browser tests for WebAssembly redirect round-trip, PKCE, exact-origin CORS, no client secret, transaction cleanup, memory token loss on reload, and chooser accessibility.
- Cross-repository tests run both Studio hosts against the same fake provider and Core broker fixture.