Appearance
Authentication & User Management
Overview
Authentication and user management form the foundational security layer of the Progressive World-Building Platform. Every API request -- whether creating a timeline, inviting a collaborator, or viewing a character sheet -- must pass through authentication to establish the identity of the caller, and that identity is then used across the entire system for authorization decisions.
This feature handles user registration, login, session management via JWT tokens, password resets, profile management, and the critical intersection between authentication and the invitation system. When a Game Master invites a player by email, the platform must gracefully handle both cases: the invitee already has an account, or they need to register first. Authentication is the gateway that makes this possible.
Because the auth provider is a pending team decision (ASP.NET Identity, Auth0/Clerk, or Firebase Auth), this document describes the authentication contract -- the behaviors and interfaces the rest of the system depends on -- rather than a specific implementation. Regardless of provider choice, the application maintains its own user records, role-based authorization logic, and permission checks.
Goals
- Provide secure user registration with email verification
- Issue and manage short-lived access tokens with long-lived refresh tokens
- Support password reset via email-based token flow
- Establish a stable internal user identity (UUIDv7) that the rest of the system references
- Bridge external auth provider identities to internal user records
- Enable seamless invitation acceptance for both new and existing users
- Expose a clean
ICurrentUserServiceabstraction so all other features can access auth context without coupling to the auth provider - Support future multi-provider authentication (e.g., adding OAuth providers later)
User Stories
As a Game Master, I want to register an account so I can create and manage my campaign worlds.
As a Game Master, I want to invite players by email so they can join my project, whether or not they already have an account.
As a Writer, I want to log in and receive a token so I can access my projects across sessions without re-entering credentials constantly.
As a Player, I want to click an invitation link, register if needed, and land directly in the project I was invited to.
As a User, I want to reset my password if I forget it, without losing access to my projects or roles.
As a User, I want to update my display name and profile details so collaborators can identify me.
As a Developer, I want a consistent auth context available in every request so I can check permissions without worrying about which auth provider is behind it.
Functional Description
User Registration
Registration creates a new user account and an internal user record. The internal record is the source of truth for the platform -- all project memberships, content ownership, and permissions reference the internal user ID (UUIDv7).
Registration fields:
- Email address (required, unique across platform)
- Password (required, minimum 8 characters, complexity rules enforced by provider)
- Display name (required, 2-50 characters)
Registration behavior:
- Validate input (email format, password strength, display name length)
- Create the account with the auth provider (or internal identity system)
- Create an internal
Userrecord with a new UUIDv7 and link to the external provider identity - Send a verification email with a time-limited token
- Return a success response (user must verify email before logging in)
- If the registration email matches a pending project invitation, do NOT auto-accept -- the user must explicitly accept after verifying and logging in
Email verification:
- Verification tokens expire after 24 hours
- Users can request a new verification email (rate-limited to 3 per hour)
- Unverified accounts cannot log in
- Unverified accounts are automatically purged after 7 days
Login & Session Management
Login authenticates a user and issues a token pair (access token + refresh token) that the client uses for subsequent API requests.
Login fields:
- Email address
- Password
Login behavior:
- Validate credentials against the auth provider
- Reject if email is not verified
- Reject if account is locked (too many failed attempts)
- Issue an access token (JWT, short-lived) and a refresh token (opaque, long-lived)
- Record the login event (timestamp, IP, user agent) for audit purposes
- Return the token pair and basic user profile
Token characteristics:
| Token | Format | Lifetime | Storage |
|---|---|---|---|
| Access Token | JWT | 15 minutes | Client memory (not localStorage) |
| Refresh Token | Opaque string | 7 days | HTTP-only secure cookie |
JWT claims:
sub-- internal user ID (UUIDv7)email-- user emailname-- display nameiat-- issued atexp-- expirationjti-- unique token ID (for revocation checks)
Account lockout:
- After 5 consecutive failed login attempts, lock the account for 15 minutes
- After 10 cumulative failed attempts in 24 hours, lock for 1 hour
- Lockout resets on successful login or password reset
Token Refresh
The refresh mechanism lets clients obtain new access tokens without re-entering credentials. This is the primary mechanism for maintaining sessions.
Refresh behavior:
- Client sends the refresh token (from HTTP-only cookie)
- Validate the refresh token (not expired, not revoked, matches a known session)
- Issue a new access token and optionally rotate the refresh token
- If the refresh token is expired or revoked, return 401 -- client must re-authenticate
Refresh token rotation:
- Each refresh issues a new refresh token and invalidates the old one
- If a previously-invalidated refresh token is used (replay detection), revoke the entire token family and force re-authentication
- This limits the damage window if a refresh token is compromised
Password Reset
Password reset allows users to regain access via a verified email address.
Reset request:
- User provides their email address
- If the email exists, send a reset link with a time-limited token
- Always return a generic success message (do not reveal whether the email exists)
- Reset tokens expire after 1 hour
- Rate limit: 3 reset requests per email per hour
Reset completion:
- User clicks the link and provides a new password
- Validate the reset token (not expired, not already used, matches email)
- Update the password with the auth provider
- Invalidate all existing refresh tokens for the user (force re-login on all devices)
- Send a confirmation email that the password was changed
- Return success -- user must log in with the new password
Invitation Acceptance Flow
This is the critical integration point between authentication and the project membership system (see Project Management). When a project member invites someone by email, the invitation flow varies based on whether the invitee already has an account.
Invitation link structure:
- Links contain an invitation token that encodes: project ID, invited email, inviter ID, expiry
- Links point to a frontend route that determines the correct flow
Existing user flow:
- User clicks invitation link
- Frontend detects the user is already logged in (or prompts login)
- Frontend calls the invitation acceptance endpoint with the invitation token
- Backend validates: token not expired, email matches logged-in user, invitation still pending
- Backend updates the
ProjectMemberrecord: status changes frompendingtoaccepted,joinedAtis set - User is redirected to the project
New user flow:
- User clicks invitation link
- Frontend detects no active session and shows registration form, preserving the invitation token
- User registers (standard registration flow)
- After email verification and first login, the frontend automatically calls the invitation acceptance endpoint
- Same acceptance logic as existing user flow
- User is redirected to the project
Edge cases for invitations:
- If the invitation has expired, show a clear message and suggest contacting the project owner
- If the invitation was already accepted, redirect to the project
- If the invitation was declined, show a message that the invitation is no longer valid
- If the email on the invitation does not match the logged-in user, reject with a clear error
Account Management
Users can update their profile information after authentication.
Updatable fields:
- Display name (2-50 characters)
- Avatar URL (optional, validated URL format)
- Bio (optional, max 500 characters)
Non-updatable fields:
- Email (change requires a separate verification flow, deferred to a later phase)
- Internal user ID
Account deletion:
- Soft-delete: marks the account as deactivated
- User's display name in existing content is replaced with "Deleted User"
- Ownership of projects must be transferred before deletion (block deletion if user owns projects)
- After 30 days, hard-delete all personal data (GDPR compliance)
- Project memberships are removed; content authored by the user remains but is de-attributed
Auth Context Integration
Every API request must carry auth context. This is how authentication integrates with the rest of the system.
ICurrentUserService: The platform exposes an ICurrentUserService (resolved per-request) that any feature can inject to get:
UserId-- the internal UUIDv7Email-- the verified emailDisplayName-- current display nameIsAuthenticated-- whether the request has a valid token
This service is populated by authentication middleware that validates the JWT on every request.
Middleware pipeline:
- JWT validation middleware extracts and validates the access token
- If valid, populates
ICurrentUserServicewith claims - If invalid or missing,
ICurrentUserService.IsAuthenticatedreturns false - Authorization attributes on endpoints enforce that the user is authenticated
- Feature-level permission checks (via
IPermissionService) use the user ID fromICurrentUserServiceto check project roles
Data Flow
Registration Sequence
Login Sequence
Password Reset Flow
Invitation Acceptance Flow
Token Lifecycle State Diagram
Key Components
Auth Service
Wraps the chosen auth provider behind a consistent interface. Handles credential validation, account creation, password management, and token issuance. This is the only component that directly communicates with the external auth provider (or internal identity system). All other services interact through this abstraction.
Token Service
Manages the lifecycle of access tokens and refresh tokens. Responsible for JWT creation with the correct claims, refresh token generation, rotation, storage, and revocation. Implements replay detection by tracking token families.
User Service
Manages internal user records. Handles profile updates, account deactivation, and the mapping between external provider identities and internal UUIDv7 user IDs. This is the service other features call when they need user information.
Current User Service
A per-request scoped service populated by middleware. Provides the authenticated user's ID, email, and display name to any component that needs it. This is the primary integration point -- every feature depends on this service rather than reading tokens directly.
Email Service
Sends transactional emails: verification, password reset, password-changed confirmation. Delegates to an email provider (SendGrid, SES, etc.). Emails are sent asynchronously to avoid blocking API responses.
Invitation Bridge
A thin coordination layer that connects the auth system to the project membership system. When a user logs in or registers, this component checks for pending invitations associated with their email and surfaces them to the frontend. It does NOT auto-accept invitations -- acceptance is always an explicit user action.
Feature Interactions
| Feature | Interaction |
|---|---|
| Project Management | Projects reference ownerUserId. The invitation system depends on auth to validate invitee identity. |
| Permission & Visibility | IPermissionService depends on ICurrentUserService to get the user ID for role lookups. |
| Real-time & Collaboration | SignalR connections authenticate via the same JWT. The hub validates the token on connection. |
| Content Features | All content creation records createdByUserId from ICurrentUserService. |
| Version History | Change tracking records which user made each change via ICurrentUserService. |
Edge Cases & Error Handling
Duplicate registration email: Return a generic error ("Unable to create account") to avoid revealing whether the email is already registered. Do not distinguish between "email taken" and other failures in the response.
Concurrent registration with same email: The auth provider and database unique constraint handle this. The second request will fail with a conflict, surfaced as a generic registration error.
Expired verification token: User requests a new verification email. The old token is invalidated.
Token refresh during deployment: If the signing key rotates during a deployment, all existing access tokens become invalid. Clients will use their refresh tokens to get new access tokens signed with the new key. Refresh tokens remain valid because they are validated by database lookup, not signature.
Auth provider outage: If the external auth provider is unavailable, login and registration fail with a 503 Service Unavailable. Existing sessions continue to work as long as their access tokens are valid (JWT validation is local). Refresh will fail until the provider recovers.
User deletes account while holding project ownership: Block the deletion. Return a 409 Conflict with a message listing the projects that must be transferred first.
Invitation token reuse after acceptance: Return 200 with the project key (idempotent). Do not create a duplicate membership.
Race condition on invitation acceptance: Use database-level constraints (unique on projectId + userId in ProjectMember) to prevent duplicate memberships if two acceptance requests arrive simultaneously.
Phase & Priority
Phase 1: Core Timeline & Projects
Authentication is a Phase 1 prerequisite -- nothing else works without it. It should be the first feature implemented.
Priority order within this feature:
- Registration + email verification
- Login + JWT issuance
- Token refresh + rotation
ICurrentUserServicemiddleware integration- Password reset
- Profile management
- Account deletion (can be deferred to late Phase 1)