Skip to content

Commit 2b1d15d

Browse files
committed
Revise milestone 5 docs for admin code management
1 parent 03330d5 commit 2b1d15d

14 files changed

+501
-326
lines changed

docs/SPEC.md

Lines changed: 228 additions & 89 deletions
Large diffs are not rendered by default.

docs/tasks/milestone-5/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# Milestone 5: Beta Onboarding and Nickname Identity
1+
# Milestone 5: Beta Onboarding, Internal Admin Auth, and Invitation Code Management
22

3-
This directory contains the milestone and task documents for the beta onboarding and nickname identity rollout.
3+
This directory contains the milestone and task documents for the revised Milestone 5 rollout.
44

5-
- Milestone doc: [Milestone 5: Beta Onboarding and Nickname Identity](./milestone-5-beta-onboarding-and-nickname-identity.md)
6-
- Task 01: [User Identity and Beta Access Foundation](./task-01-user-identity-and-beta-access-foundation.md)
7-
- Task 02: [Signup Completion and Auth Gating](./task-02-signup-completion-and-auth-gating.md)
8-
- Task 03: [Nickname Profile and Public Shelf Sharing](./task-03-nickname-profile-and-public-shelf-sharing.md)
9-
- Task 04: [Nickname-Based Club Invitation Flow](./task-04-nickname-based-club-invitation-flow.md)
5+
- Milestone doc: [Milestone 5: Beta Onboarding, Internal Admin Auth, and Invitation Code Management](./milestone-5-beta-onboarding-admin-auth-and-invitation-codes.md)
6+
- Task 01: [User and Admin Identity Foundation](./task-01-user-and-admin-identity-foundation.md)
7+
- Task 02: [Signup Completion and Public App Auth Gating](./task-02-signup-completion-and-public-app-auth-gating.md)
8+
- Task 03: [Internal Admin Auth and Invitation Code Management](./task-03-internal-admin-auth-and-invitation-code-management.md)
9+
- Task 04: [Nickname Profile, Public Shelf Sharing, and Club Invites](./task-04-nickname-profile-public-shelf-sharing-and-club-invites.md)
1010
- Task 05: [Quality Gates and Regression Coverage](./task-05-quality-gates-and-regression-coverage.md)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Milestone 5: Beta Onboarding, Internal Admin Auth, and Invitation Code Management
2+
3+
## Goal
4+
Deliver the closed-beta onboarding and service-native identity workflow on top of the existing auth, books, clubs, threads, shelves, and reviews foundation, while adding an internal-only admin panel for invitation-code management:
5+
6+
- require completed signup after OAuth before public app access
7+
- collect a stable nickname, gender, country, favorite genres, and an invitation code
8+
- use nickname as the public app identity across profile, shelves, clubs, threads, and reviews
9+
- change public shelf sharing to nickname-based URLs
10+
- change private-club invite targeting from email to nickname-resolved user identity
11+
- add internal admin auth and admin UI for DB-backed invitation-code management
12+
13+
## Current Baseline
14+
- `bookapp.users` currently stores provider metadata only and Google login immediately creates a usable app user.
15+
- Milestones 2 through 4 already provide clubs, discussions, shelves, and reviews on top of the current auth foundation.
16+
- The app has no internal-admin auth path, no admin route tree, and no credentials-based sign-in.
17+
- Beta access is currently documented as a shared env-backed code rather than a database-managed domain.
18+
- Private club invites are email-targeted and invite acceptance still matches by email or user id.
19+
- Public shelf routes are keyed by `userId`, not nickname.
20+
21+
## Planning Assumptions
22+
- Internal admins are manually created in Supabase UI and stored with `provider = 'internal'`.
23+
- Internal admins use email/password only and do not participate in public signup or the public product routes.
24+
- Invitation codes are future-ready through `purpose`, optional expiry, and optional max uses, but only `BETA_SIGNUP` redemption is implemented in Milestone 5.
25+
- Nickname is immutable in Milestone 5 and is validated as a lowercase URL-safe handle.
26+
- Private club invites remain a separate domain from admin-managed invitation codes.
27+
- Milestone 5 does not add a public profile route, nickname change UI, or in-app admin-user bootstrap flow.
28+
29+
## Delivery Order
30+
1. [Task 01: User and Admin Identity Foundation](./task-01-user-and-admin-identity-foundation.md)
31+
2. [Task 02: Signup Completion and Public App Auth Gating](./task-02-signup-completion-and-public-app-auth-gating.md)
32+
3. [Task 03: Internal Admin Auth and Invitation Code Management](./task-03-internal-admin-auth-and-invitation-code-management.md)
33+
4. [Task 04: Nickname Profile, Public Shelf Sharing, and Club Invites](./task-04-nickname-profile-public-shelf-sharing-and-club-invites.md)
34+
5. [Task 05: Quality Gates and Regression Coverage](./task-05-quality-gates-and-regression-coverage.md)
35+
36+
## Milestone Exit Criteria
37+
- OAuth-authenticated public users cannot reach the reader app until they complete Book by Book signup.
38+
- Completing signup persists nickname, gender, country, favorite genres, and signup completion state, and atomically redeems a valid `BETA_SIGNUP` invitation code.
39+
- Internal admins can sign in through `/admin/signin` and manage invitation codes from `/admin/invitation-codes`.
40+
- Invitation codes are stored hashed, support active/inactive state, and can optionally expire or cap uses.
41+
- Nickname becomes the default user-facing identity across `/me`, shelves, clubs, threads, reviews, and invite pages.
42+
- Public shelf sharing works by nickname route while preserving the existing signed-in-only public shelf access model.
43+
- Private club invites are created by nickname and accepted only by the targeted signed-in public user.
44+
- Milestone 5 passes `pnpm lint`, `pnpm build`, `pnpm test`, `pnpm test:integration`, and required Playwright coverage for onboarding, admin auth, invitation-code management, nickname routing, and invite flows.

docs/tasks/milestone-5/milestone-5-beta-onboarding-and-nickname-identity.md

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Task 01: User and Admin Identity Foundation
2+
3+
## Goal
4+
Add the schema, shared validation, repository contracts, and identity helpers needed for every Milestone 5 public-user onboarding, internal-admin auth, invitation-code, sharing, and invite flow.
5+
6+
## Scope
7+
- Extend the app-facing database shape and domain contracts for public users:
8+
- `nickname`
9+
- `gender`
10+
- `countryCode`
11+
- `favoriteGenres`
12+
- `signupCompletedAt`
13+
- Extend the `users` model for internal admins with a nullable `passwordHash` used only for `provider = 'internal'`.
14+
- Add the schema migration and source-of-truth schema updates needed to persist:
15+
- the new public-user profile fields on `bookapp.users`
16+
- internal admin password-hash support on `bookapp.users`
17+
- `invitation_codes`
18+
- `invitation_code_redemptions`
19+
- Define shared validation and normalization helpers for:
20+
- nickname format and uniqueness
21+
- gender enum parsing
22+
- ISO country selection
23+
- favorite-genre allowlist validation
24+
- internal admin email normalization
25+
- invitation-code hashing and lookup inputs
26+
- Define shared domain contracts for:
27+
- `InvitationCodePurpose`
28+
- `InvitationCodeRecord`
29+
- `InvitationCodeRedemptionRecord`
30+
- public user vs internal admin session identity
31+
- Update auth and user repository contracts so app identity no longer depends on provider email for normal signed-in flows.
32+
33+
## Implementation Notes
34+
- Keep the public profile fields and internal password hash on `bookapp.users`; do not introduce a separate profile table or admin table in Milestone 5.
35+
- `signup_completed_at` remains the source of truth for whether a public signed-in user can use the reader app.
36+
- Internal admins are represented by:
37+
- `provider = 'internal'`
38+
- `provider_user_id = normalized email`
39+
- password-hash verification
40+
- Internal admins are manually created in Supabase UI and are not provisioned by the app.
41+
- OAuth-linked public users still use `auth_accounts`; internal credentials auth can resolve directly from `users`.
42+
- Invitation codes must be stored hashed and modeled for future purposes even though only `BETA_SIGNUP` is redeemed in this milestone.
43+
44+
## Acceptance Criteria
45+
- The database schema and app-facing types expose all required Milestone 5 public-user, internal-admin, and invitation-code fields.
46+
- Shared validation covers nickname normalization, allowed genre values, required country/gender selection, internal admin auth inputs, and invitation-code hashing contracts.
47+
- Reusable repository helpers can distinguish incomplete public users, completed public users, and internal admins without route-local duplication.
48+
- Shared display helpers make nickname the first-class reader-facing identity.
49+
- Shared code-management types are stable enough to support future invitation-code purposes without revisiting the schema shape.
50+
51+
## Expected Touchpoints
52+
- `db/schema/data.sql`
53+
- `db/migrations/*`
54+
- `types/db/index.ts`
55+
- `lib/auth/*`
56+
- `tests/unit/auth*.test.ts`
57+
- invitation-code validation and repository helpers

docs/tasks/milestone-5/task-01-user-identity-and-beta-access-foundation.md

Lines changed: 0 additions & 44 deletions
This file was deleted.

docs/tasks/milestone-5/task-02-signup-completion-and-auth-gating.md

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Task 02: Signup Completion and Public App Auth Gating
2+
3+
## Goal
4+
Implement the completed-signup flow so authenticated but incomplete public users are routed through `/signup` before they can use the reader-facing app.
5+
6+
## Scope
7+
- Add `/signup` as the authenticated onboarding route for incomplete public users.
8+
- Build the signup-completion form for:
9+
- nickname
10+
- gender
11+
- country
12+
- favorite genres
13+
- invitation code
14+
- Add the `completeSignup({ nickname, gender, countryCode, favoriteGenres, invitationCode, callbackUrl? })` mutation.
15+
- Validate and redeem an active `BETA_SIGNUP` invitation code transactionally during signup completion.
16+
- Preserve safe callback URLs from:
17+
- sign-in redirects
18+
- invite acceptance entry points
19+
- direct protected-page hits by incomplete public users
20+
- Redirect incomplete public users away from reader-facing protected routes and protected mutations until `signup_completed_at` is set.
21+
- Redirect completed public users away from `/signup` to the callback destination or `/books/search`.
22+
23+
## Implementation Notes
24+
- Keep Google OAuth as the only public-user provider for Milestone 5, but treat OAuth and completed signup as separate lifecycle steps.
25+
- The onboarding route should require an authenticated public session; signed-out users still go to `/signin`.
26+
- Internal admins should never use `/signup`; route guards should redirect them to `/admin/invitation-codes`.
27+
- Reuse existing safe-return URL handling patterns rather than accepting arbitrary external redirects.
28+
- Invite links and other protected deep links should survive the onboarding detour by preserving callback intent.
29+
- Sign-out remains available to incomplete public users.
30+
- Invitation-code redemption should record audit history and enforce inactive, expired, exhausted, and wrong-purpose rejections on the server.
31+
32+
## Acceptance Criteria
33+
- A newly authenticated but incomplete public user is redirected to `/signup` before any reader-facing app surface renders.
34+
- Completing signup writes the required profile fields, marks signup complete, redeems the code, and redirects the user back to the intended destination.
35+
- Protected reader-facing server actions reject incomplete public users consistently.
36+
- Completed public users cannot accidentally return to `/signup` as a normal app page.
37+
- Invalid, inactive, expired, exhausted, and wrong-purpose invitation codes are rejected with clear server-enforced behavior.
38+
39+
## Expected Touchpoints
40+
- `app/signup/page.tsx`
41+
- `app/signin/page.tsx`
42+
- `app/auth/error/page.tsx`
43+
- `app/api/auth/[...nextauth]/route.ts`
44+
- `proxy.ts`
45+
- `lib/auth/server.ts`
46+
- signup-completion server actions and tests

0 commit comments

Comments
 (0)