Skip to content

feat: let consuming apps configure the login features (2FA / passkey / registration)#67

Merged
andregoepel merged 2 commits into
mainfrom
feat/configurable-login-features
Jul 5, 2026
Merged

feat: let consuming apps configure the login features (2FA / passkey / registration)#67
andregoepel merged 2 commits into
mainfrom
feat/configurable-login-features

Conversation

@andregoepel

Copy link
Copy Markdown
Owner

Surfaces marten-identity's new registration / two-factor / passkey feature flags to AppFoundation host apps.

What

  • AppFoundationOptions.ConfigureIdentity — a passthrough Action<MartenIdentityBlazorOptions> forwarded to AddMartenIdentityBlazor. A consuming app toggles the login features (and any other identity UI option) from AddAppFoundation:

    builder.AddAppFoundation(o => o.ConfigureIdentity = i =>
    {
        i.EnableUserRegistration = false;
        i.EnablePasskey = false;
    });
  • Enforcement: UseAppFoundation now wires UseMartenIdentityFeatureGate(), so a disabled feature's pages and endpoints return 404 / redirect — unreachable by direct URL, not merely hidden.

  • Shell UI: the management NavMenu hides the Two-factor authentication and Passkeys entries when those features are disabled (registration has no app-foundation menu entry — the identity Login page owns that link).

Defaults are unchanged: all three features remain enabled unless a host opts out.

Dependency

Bumps AndreGoepel.Marten.Identity.Blazor to 1.2.0-preview1, which introduces the feature-flag API (MartenIdentityBlazorOptions.Enable*, IIdentityFeatureProvider, UseMartenIdentityFeatureGate). Continues @andregoepel's in-progress version bump on main.

⚠️ This pins a preview package. app-foundation shouldn't cut a stable release until marten-identity 1.2.0 final is published and this is bumped off the preview.

Tests

AddAppFoundationIdentityTests: ConfigureIdentity flows through to the resolved MartenIdentityBlazorOptions; without it, the full feature set stays enabled.

Verification

  • dotnet restore --locked-mode — clean (lock files updated to 1.2.0-preview1).
  • dotnet build -c Release — 0 errors; dotnet test -c Release — 82/82.
  • csharpier check passes on the changed C# files.

Note for the host (andregoepel.dev)

A host that wants a persistent, admin-editable toggle (rather than config-time) can register its own IIdentityFeatureProvider — the default OptionsIdentityFeatureProvider is TryAdd-registered, so a host implementation takes precedence.

Surface marten-identity's registration / 2FA / passkey feature flags to
AppFoundation hosts:

- New AppFoundationOptions.ConfigureIdentity forwards to
  AddMartenIdentityBlazor, so a host can toggle EnableUserRegistration /
  EnableTwoFactor / EnablePasskey (and any other MartenIdentityBlazorOptions).
- UseAppFoundation now wires UseMartenIdentityFeatureGate, so a disabled
  feature's pages/endpoints are unreachable by direct URL, not just hidden.
- The management NavMenu hides the Two-factor and Passkeys entries when
  those features are disabled.

Bumps AndreGoepel.Marten.Identity.Blazor to 1.2.0-preview1, which
introduces the feature-flag API.
Make the registration / 2FA / passkey flags admin-editable at runtime,
persisted, on top of the ConfigureIdentity baseline:

- IdentityFeatureSettingsDocument (Marten) + IIdentityFeatureSettingsStore
  persist the flags; MartenIdentityFeatureProvider replaces the identity
  package's options-only IIdentityFeatureProvider so the feature gate and
  nav menu honour the saved values (falling back to the configured baseline
  when no record exists yet).
- New Administration > Login Features page (Administrator only) to toggle
  each feature.
- AddAppFoundationIdentityFeatures wires it, registered after
  AddMartenIdentityBlazor so the database-backed provider takes precedence.
@andregoepel

Copy link
Copy Markdown
Owner Author

Extended this PR to make the flags admin-editable at runtime (persisted), not just config-time:

  • IdentityFeatureSettingsDocument (Marten) + IIdentityFeatureSettingsStore persist the flags; MartenIdentityFeatureProvider replaces the identity package's options-only IIdentityFeatureProvider (registered after AddMartenIdentityBlazor, last-wins), so the feature gate and nav menu honour saved values — falling back to the ConfigureIdentity baseline until an admin saves.
  • New Administration → Login Features page (/Administration/LoginFeatures, Administrator-only) with a switch per feature + a "configuration baseline" hint until first save.
  • AddAppFoundationIdentityFeatures() wires it from AddAppFoundation.

So the model is: ConfigureIdentity sets the baseline; the admin page overrides it at runtime with no redeploy. Same precedence contract the identity package documents (a registered IIdentityFeatureProvider beats the options default).

Tests: bUnit page test (load/save/route/authorize) + provider mapping test. 87/87 passing, locked restore + csharpier clean.

@andregoepel
andregoepel merged commit 4952036 into main Jul 5, 2026
3 checks passed
andregoepel added a commit that referenced this pull request Jul 5, 2026
…#68)

* feat: add an Aspire sample host for local testing and as a setup demo

Add a runnable example under samples/:

- AndreGoepel.AppFoundation.Sample — a minimal Blazor Server host that wires
  the packages exactly as the README describes (AddAppFoundation /
  UseAppFoundation, App.razor, Routes.razor, MapAdditionalIdentityEndpoints).
- AndreGoepel.AppFoundation.AppHost — a .NET Aspire orchestrator that starts
  PostgreSQL (with a data volume) and the web app, wiring the
  appfoundation-database connection string.

Run with: dotnet run --project samples/AndreGoepel.AppFoundation.AppHost.

The AppHost opts out of central package management and lock files because the
Aspire SDK injects RID-specific packages that aren't portable across OSes
(would break CI locked-mode restore); the sample keeps a portable lock file.
Both are IsPackable=false. Adds a "Try it locally" README section and points
.aspire/settings.json at the in-repo AppHost.

* fix(sample): enable global interactivity so the shell menus work

The management layout (MainLayout) carries interactive click handlers — the
sidebar toggle, profile menu, and the RadzenPanelMenu in NavMenu. A layout is
rendered statically under per-page interactivity, so those handlers never wired
up and the menus appeared dead ("js not working").

Apply the render mode globally on <Routes> and <HeadOutlet> so the layout is
interactive. Verified this does not conflict with the foundation's per-page
@rendermode InteractiveServer pages (a matching descendant render mode is
allowed).

* chore(sample): sync lockfile to identity 1.2.0-preview1 (stacked on #67)

* update aspire packages

* fix(sample): align Aspire.Hosting.AppHost to 13.4.6 with the SDK

* fix(sample): opt the web sample out of lock files (runtime-tied asset drift)

* style(sample): apply csharpier formatting to the sample csproj
@andregoepel
andregoepel deleted the feat/configurable-login-features branch July 5, 2026 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant