docs: correct Phase 19 to CMS-only host model (drop DeepDrftPublic track)
All three AuthBlocks account paths live on DeepDrftManager; public registration is an unauthenticated CMS route like the CMS login. Path 2 reduces to a single auth-state-driven DefaultLayout fix (SkipperHaven pattern).
This commit is contained in:
@@ -380,91 +380,104 @@ opacity + muted-text mixes are tune-on-screen details, not decision gates.
|
||||
|
||||
---
|
||||
|
||||
## Phase 19 — AuthBlocks User Management (CMS admin + public self-registration)
|
||||
## Phase 19 — AuthBlocks User Management (CMS-only: admin surfaces + public self-registration)
|
||||
|
||||
Wire **all three** AuthBlocks account-creation paths into DeepDrft, each on its correct host: the
|
||||
CMS-side user-administration surface (provision users, manage accounts, manage registration invites,
|
||||
manage role permissions) on `DeepDrftManager`, **and** the public-facing self-service registration form
|
||||
on `DeepDrftPublic`. Daniel's framing: *"already part of the AuthBlocks library so we just wire it up."*
|
||||
Correct for the CMS — and **further along than it implies** there; the public-site path is a genuine
|
||||
cold-start integration. Full design, the verified three-path model, the already-done-vs-remaining split,
|
||||
the public-site cold-start analysis, scope boundaries, and open questions:
|
||||
`product-notes/phase-19-user-management-cms.md`.
|
||||
Wire **all three** AuthBlocks account-creation paths into the `DeepDrftManager` CMS — the admin
|
||||
user-administration surface (provision users, manage accounts, manage registration invites, manage role
|
||||
permissions) **and** the public-facing self-service registration form. **All three paths live on
|
||||
`DeepDrftManager` (the CMS app); there are NO changes to `DeepDrftPublic` in this phase.** Daniel's
|
||||
framing: *"already part of the AuthBlocks library so we just wire it up."* Correct — and **further along
|
||||
than it implies**: almost everything landed by side-effect of the prior startup separation. Full design,
|
||||
the verified three-path model, the already-done-vs-remaining split, the SkipperHaven pattern + concrete
|
||||
deltas, scope boundaries, and open questions: `product-notes/phase-19-user-management-cms.md`.
|
||||
|
||||
**The three account-creation paths (verified against AuthBlocks source 2026-06-19):**
|
||||
**The three account-creation paths (verified against AuthBlocks source 2026-06-19) — ALL CMS routes:**
|
||||
1. **Admin provisions directly** — `SuperRegister.razor` → `/account/superregister` → `POST
|
||||
api/auth/admin-register` (UserAdmin-gated, **working**). Host: **CMS**. Creates a live account now.
|
||||
api/auth/admin-register` (UserAdmin-gated, **working**). Creates a live account now.
|
||||
2. **Public self-service** — `Register.razor` → `/account/register` → `POST api/auth/register`
|
||||
(unauthenticated, **working**). Host: **PUBLIC SITE**. Invited user redeems a code (pre-filled from the
|
||||
invite email's deep link) and self-registers.
|
||||
(**unauthenticated, no role gate, working**). A **public-facing CMS route, exactly like the CMS
|
||||
`/account/login` page** — invited user redeems a code (pre-filled from the invite email's deep link)
|
||||
and self-registers, all on the CMS host.
|
||||
3. **Admin provisions a token + triggers the invite email** — `NewRegistration(Form).razor` →
|
||||
`/useradmin/registrations/new` → `POST api/pendingregistration/create` (UserAdmin-gated). Host:
|
||||
**CMS**. **Sends a real email server-side** via Mailtrap (`RegistrationEmailTemplate` +
|
||||
`IGeneralEmailSender`, configured in DeepDrftAPI from `environment/authblocks.json`) — **not stubbed.**
|
||||
`/useradmin/registrations/new` → `POST api/pendingregistration/create` (UserAdmin-gated). **Sends a
|
||||
real email server-side** via Mailtrap (`RegistrationEmailTemplate` + `IGeneralEmailSender`, configured
|
||||
in DeepDrftAPI from `environment/authblocks.json`) — **not stubbed.**
|
||||
|
||||
**Scope reversal (Daniel, 2026-06-19).** Rev. 1 deferred public registration and treated "create user"
|
||||
as one CMS path. Both reversed: all three paths are in scope, and public registration (path 2) is now a
|
||||
distinct **public-site track**. The only genuinely stubbed surface is **Reset Password** (`Users.razor`,
|
||||
`// todo integrate with email`; **no backing endpoint** in `AuthRoutes`) — handled separately by Daniel
|
||||
in the AuthBlocks repo (see `product-notes/authblocks-password-reset-brief.md`).
|
||||
**Host-model correction (Daniel, 2026-06-19).** A prior revision placed public registration (path 2) on
|
||||
`DeepDrftPublic` as a cold-start integration. **Wrong — there are NO `DeepDrftPublic` changes.** Public
|
||||
registration is an unauthenticated route *on the CMS app*, mirroring the CMS's already-public
|
||||
`/account/login`. The only genuinely stubbed surface is **Reset Password** (`Users.razor`, `// todo`; **no
|
||||
backing endpoint** in `AuthRoutes`) — handled separately by Daniel in the AuthBlocks repo (see
|
||||
`product-notes/authblocks-password-reset-brief.md`).
|
||||
|
||||
**CMS side — most wiring already landed by side-effect.** The AuthBlocks startup separation
|
||||
(`PLAN_authblocks_trackmanager.md`, 2026-05-25) + login/logout integration already put the entire
|
||||
user-admin surface in place on `DeepDrftManager`: `Cerebellum.AuthBlocks.Web` referenced,
|
||||
`ConfigureAuthServices` registers every client + ViewModel pointed at DeepDrftAPI, the router discovers
|
||||
the pages (`AdditionalAssemblies`), they render in `CmsLayout` (`DefaultLayout`), and the DeepDrft
|
||||
`Admin` role **inherits** `UserAdmin` (the seeded admin passes the gate with no role change). The pages
|
||||
ship in a published **RCL**, so the worried-about "extract pages into an RCL" fork **does not arise**.
|
||||
The CMS remaining work is exposure + verification + polish — the surface is invisible only because
|
||||
`CmsLayout` has **no nav menu** (app bar + Home button), so nothing links to `/useradmin/*` or
|
||||
`/account/superregister`.
|
||||
**Most wiring already landed by side-effect.** The AuthBlocks startup separation
|
||||
(`PLAN_authblocks_trackmanager.md`, 2026-05-25) + login/logout integration already put the entire surface
|
||||
in place on `DeepDrftManager`: `Cerebellum.AuthBlocks.Web` referenced, `ConfigureAuthServices` registers
|
||||
every client + ViewModel **and** the `JwtAuthenticationStateProvider` path 2 needs, the router discovers
|
||||
every page (`AdditionalAssemblies`) — **including the public `/account/register`** — and the DeepDrft
|
||||
`Admin` role **inherits** `UserAdmin` (the seeded admin passes the gate with no change). The pages ship in
|
||||
a published **RCL**, so the worried-about "extract pages into an RCL" fork **does not arise**.
|
||||
|
||||
**Public side — genuine cold start.** `DeepDrftPublic` has **no AuthBlocks footprint at all** (verified:
|
||||
no package ref, no `ConfigureAuthServices`, no page discovery). Path 2 requires real host integration:
|
||||
package ref + service wiring + page discovery + layout + CORS verification. The render-mode substrate is
|
||||
compatible (the public site already has InteractiveServer, which `Register.razor` needs).
|
||||
**Two real gaps remain.** (a) **No nav** — `CmsLayout` is just an app bar + Home button, so nothing links
|
||||
to `/useradmin/*` or `/account/superregister` (admin surface invisible). (b) **Wrong layout for public
|
||||
pages** — `Routes.razor` uses a **static** `DefaultLayout="typeof(CmsLayout)"`, so an unauthenticated
|
||||
visitor to `/account/register` (or `/account/login`) lands in the authenticated app shell instead of the
|
||||
lean splash.
|
||||
|
||||
**Two parallel tracks.** CMS track `19.1 → {19.2, 19.3}`; public track `19.4` independent and parallel.
|
||||
**SkipperHaven is the canonical pattern.** `SkipperHaven` (same AuthBlocks library) exposes login +
|
||||
register as public/unauthenticated routes correctly by making `Routes.razor`'s `DefaultLayout`
|
||||
**auth-state-driven** — unauthenticated → home/lean layout, authenticated → app shell (resolved in
|
||||
`OnParametersSetAsync` off the cascaded `AuthenticationState`). **The concrete delta DeepDrftManager
|
||||
needs is exactly one change** (spec §2c): make its `DefaultLayout` auth-state-driven, resolving
|
||||
`CmsHomeLayout` (unauth) vs. `CmsLayout` (auth). Everything else SkipperHaven does — service wiring, page
|
||||
discovery, both layouts — DeepDrftManager **already has** (it even already ships `CmsHomeLayout`, used by
|
||||
the `/` home splash). So path 2 is **one router edit**, not a host integration.
|
||||
|
||||
*CMS track:*
|
||||
- **19.1 — CmsLayout navigation (cold-start, the only CMS code wave). DECIDED nav shape: G1-b.** Add a
|
||||
**One host (`DeepDrftManager`), two parallel tracks** (different files), then verify + theme.
|
||||
|
||||
- **19.1 — CmsLayout navigation (admin-nav track; the main code wave). DECIDED nav shape: G1-b.** Add a
|
||||
`MudDrawer` + toggle to `CmsLayout.razor`; mount the shipped `UserAdminMenu` fragment (self-gates to
|
||||
`UserAdmin`+) alongside the existing CMS destinations (Catalogue / Releases / Upload); surface **both**
|
||||
admin account paths (path 1 `SuperRegister` + path 3 via the Registrations link); do **not** surface the
|
||||
redundant bare `NewUser` (OQ2 resolved). **No service, API, data, or AuthBlocks-source change.**
|
||||
- **19.2 — End-to-end verification (after 19.1).** Exercise provision-now (path 1), **invite-email send
|
||||
(path 3)**, list/deactivate users, permissions against a running DeepDrftAPI; confirm cross-host token +
|
||||
CORS and that the Mailtrap creds are real. Mostly test; any break is likely a one-line config fix or an
|
||||
upstream AuthBlocks issue.
|
||||
- **19.3 — Theming legibility sweep (after 19.1, parallel-ok).** Accept the CMS palette for the
|
||||
MudBlazor-default grids; fix only contrast/legibility breaks. Bespoke restyle deferred.
|
||||
|
||||
*Public-site track:*
|
||||
- **19.4 — Public self-service registration on DeepDrftPublic (cold-start, parallel to 19.1).** Wire path
|
||||
2: add `Cerebellum.AuthBlocks.Web` to `DeepDrftPublic`, call `ConfigureAuthServices` in `Program.cs`
|
||||
pointed at the existing DeepDrftAPI base URL, add page discovery so `/account/register` is reachable,
|
||||
settle layout (OQ8) + route-exposure posture (OQ7), verify CORS for the public origin. Real
|
||||
host-integration code (unlike the CMS). Acceptance: the full path-3→path-2 loop works — admin provisions
|
||||
in CMS → email arrives → invited user redeems on the public site.
|
||||
redundant bare `NewUser` (OQ2 resolved). Scope: `CmsLayout.razor`. **No service, API, data, or
|
||||
AuthBlocks-source change.**
|
||||
- **19.2 — Public-route layout (public-route track; parallel to 19.1). DECIDED: G0-a.** Make
|
||||
`Routes.razor`'s `DefaultLayout` auth-state-driven (mirroring SkipperHaven, spec §2c D1): cascade
|
||||
`Task<AuthenticationState>`, resolve `_currentLayout = authed ? CmsLayout : CmsHomeLayout`, bind
|
||||
`DefaultLayout="@_currentLayout"`. This renders `/account/register` (path 2) **and** `/account/login` in
|
||||
the lean `CmsHomeLayout` for unauthenticated visitors. Scope: `Routes.razor` only. **No new layout (both
|
||||
exist), no package, no service, no AuthBlocks-source change.**
|
||||
- **19.3 — End-to-end verification (after 19.1 + 19.2).** Exercise provision-now (path 1), **invite-email
|
||||
send (path 3) incl. that the invite link `{ReturnHost}` points at the CMS origin**, list/deactivate
|
||||
users, permissions against a running DeepDrftAPI; confirm cross-host token + CORS, and **the full
|
||||
path-3→path-2 loop on the single CMS host** (admin provisions → email arrives → invitee redeems on the
|
||||
CMS `/account/register` in the lean layout). Mostly test; any break is likely a one-line config fix
|
||||
(esp. Mailtrap creds + return host) or an upstream AuthBlocks issue.
|
||||
- **19.4 — Theming legibility sweep (after 19.1 + 19.2, parallel-ok with 19.3).** Accept the CMS palette
|
||||
for the MudBlazor-default grids and the public pages now in `CmsHomeLayout`; fix only contrast/legibility
|
||||
breaks. Bespoke restyle deferred.
|
||||
|
||||
**Deferred (note, don't build):** admin dashboard landing (G1-c); working **Reset Password** (separate
|
||||
AuthBlocks-repo effort); bespoke restyle of the AuthBlocks grids; a lean public auth layout (OQ8); a
|
||||
visible public-nav Register link (OQ9 — invite-only, deep-link entry); bumping
|
||||
`Cerebellum.AuthBlocks.Web` 10.3.33 → 10.3.35 (housekeeping; if 19.4 adds the package to the public host,
|
||||
pin both hosts to one version).
|
||||
AuthBlocks-repo effort); bespoke restyle of the AuthBlocks grids; a visible public Register nav link
|
||||
(invite-only — the email deep link is the entry point); bumping `Cerebellum.AuthBlocks.Web` 10.3.33 →
|
||||
10.3.35 (housekeeping).
|
||||
|
||||
**Open questions for Daniel (spec §6).** *Resolved:* (1) nav shape **G1-b**; (2) surface path 1 +
|
||||
path 3, hide bare `NewUser`; (5) Reset Password non-functional in v1, handled separately. *Still open:*
|
||||
(3) admin dashboard defer (recommend defer); (4) package bump (recommend leave); (6) accept public site
|
||||
becoming auth-aware (recommend accept); (7) public route exposure of admin routes — present-but-gated vs.
|
||||
narrow discovery (recommend accept for v1); (8) public-registration layout — full chrome vs. lean
|
||||
(recommend full for v1); (9) public Register nav link (recommend deep-link-only). Items 6–9 shape 19.4;
|
||||
3, 4 are CMS scope/timing. None block 19.1.
|
||||
**Explicitly not needed:** any change to `DeepDrftPublic` (corrected host model — all three paths are CMS);
|
||||
extracting AuthBlocks pages into a new RCL; new DI/service wiring, role seeding, or Auth connection string
|
||||
(all present); editing the AuthBlocks `Login`/`Register` pages' layout (impossible without forking the
|
||||
RCL — G0-a fixes layout host-side instead).
|
||||
|
||||
**Open questions for Daniel (spec §6).** *Resolved:* (1) nav shape **G1-b**; (2) surface path 1 + path 3,
|
||||
hide bare `NewUser`; (5) Reset Password non-functional in v1, handled separately; (6) **host model — all
|
||||
three on the CMS, no `DeepDrftPublic` changes**; (7) **public-route layout G0-a** (auth-state-driven
|
||||
`DefaultLayout`, reusing `CmsHomeLayout`). *Still open:* (3) admin dashboard defer (recommend defer); (4)
|
||||
package bump (recommend leave); (8) a logged-in admin visiting `/account/register` sees it in the app
|
||||
shell under G0-a (recommend accept). None block 19.1 or 19.2.
|
||||
|
||||
**Adjacency to the deferred Identity / accounts backlog item (below).** That item is about *public,
|
||||
per-user* identity (favourites, listening history, playlists). This phase is *CMS-admin* account
|
||||
management only — same AuthBlocks substrate, different surface. They are not the same work; this phase
|
||||
does not satisfy or depend on that one.
|
||||
per-user* identity (favourites, listening history, playlists). This phase is *CMS* account management only
|
||||
(admin surfaces + invite-based self-registration) — same AuthBlocks substrate, different surface. They are
|
||||
not the same work; this phase does not satisfy or depend on that one.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user