- Install next-auth@beta, bcryptjs, zod, and @auth/prisma-adapter - Create auth.ts with credentials provider and JWT session strategy - Create auth.config.ts for edge-compatible middleware configuration - Add NextAuth type extensions for session and JWT - Create API route handler for authentication - Configure environment variables for NextAuth - Add AUTH_SETUP.md documentation Task: 1. Set up authentication infrastructure Requirements: 1.1, 2.1
28 KiB
Design Document: User Authentication and Sharing
Overview
This design extends the Ultimate Lists App to support multi-user authentication and collaborative list sharing. The system will implement secure user registration and login using NextAuth.js (Auth.js v5), session management with JWT tokens, and a permission-based access control system for list sharing.
The authentication system integrates seamlessly with the existing Next.js 14 App Router architecture, using Server Actions for authentication operations and middleware for route protection. The design maintains the current PWA capabilities while adding user-specific data isolation and collaborative features.
Key design decisions:
- NextAuth.js v5 for authentication (industry-standard, well-maintained, Next.js 14 compatible)
- Credentials provider for email/password authentication
- bcrypt for password hashing
- JWT sessions for stateless authentication
- Middleware-based route protection for securing pages
- Permission-based access control with owner, editor, and viewer roles
- Database-level access control to ensure data isolation
Architecture
High-Level Architecture
┌─────────────────────────────────────────────────────────┐
│ Client Browser │
│ ┌──────────────────────────────────────────────────┐ │
│ │ React Components (RSC + Client) │ │
│ │ ┌────────────┐ ┌────────────┐ ┌───────────┐ │ │
│ │ │Auth Pages │ │ Components │ │ Hooks │ │ │
│ │ │Login/ │ │ (Protected)│ │ │ │ │
│ │ │Register │ │ │ │ │ │ │
│ │ └────────────┘ └────────────┘ └───────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Service Worker (PWA) │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
│ HTTP/Server Actions
▼
┌─────────────────────────────────────────────────────────┐
│ Next.js Server │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Middleware │ │
│ │ (Route Protection & Auth) │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ NextAuth.js │ │
│ │ (Session Management & Authentication) │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Server Actions │ │
│ │ (Business Logic + Access Control) │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Prisma Client │ │
│ │ (Data Access with ACL) │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
│ SQL
▼
┌─────────────────────────────────────────────────────────┐
│ MySQL Database │
│ (Users, Lists, Items, Collaborators, Invitations) │
└─────────────────────────────────────────────────────────┘
Technology Stack
New Dependencies:
- next-auth@5.0.0-beta: Authentication framework for Next.js
- bcryptjs: Password hashing library
- @types/bcryptjs: TypeScript types for bcryptjs
- zod: Schema validation for forms and API inputs
Existing Stack:
- Framework: Next.js 14 with App Router
- Language: TypeScript
- Database: MySQL with Prisma ORM
- UI: React 18 with Tailwind CSS
Components and Interfaces
Authentication Layer
NextAuth Configuration (auth.ts)
// Core authentication configuration
export const authOptions = {
providers: [
CredentialsProvider({
credentials: { email, password },
authorize: async (credentials) => {
// Validate credentials
// Query user from database
// Verify password with bcrypt
// Return user object or null
}
})
],
session: { strategy: 'jwt' },
callbacks: {
jwt: async ({ token, user }) => {
// Add user ID to token
},
session: async ({ session, token }) => {
// Add user ID to session
}
},
pages: {
signIn: '/auth/login',
signOut: '/auth/logout',
error: '/auth/error'
}
}
Middleware (middleware.ts)
// Protect routes requiring authentication
export function middleware(request: NextRequest) {
// Check authentication status
// Redirect to login if not authenticated
// Allow access if authenticated
}
export const config = {
matcher: ['/', '/list/:path*', '/profile/:path*']
}
Authentication Actions (lib/auth-actions.ts)
registerUser(email, password, name): Create new user accountvalidateCredentials(email, password): Verify login credentialsupdatePassword(userId, currentPassword, newPassword): Change passwordupdateProfile(userId, data): Update user profiledeleteAccount(userId): Delete user and associated data
Access Control Layer
Permission Utilities (lib/permissions.ts)
type Permission = 'owner' | 'editor' | 'viewer'
// Check if user can access list
async function canAccessList(userId: string, listId: string): Promise<boolean>
// Check if user can edit list
async function canEditList(userId: string, listId: string): Promise<boolean>
// Check if user can delete list
async function canDeleteList(userId: string, listId: string): Promise<boolean>
// Get user's permission level for list
async function getPermission(userId: string, listId: string): Promise<Permission | null>
Modified Server Actions (lib/actions.ts)
All existing server actions will be modified to include access control:
getLists(userId): Get lists user owns or has access tocreateList(userId, title, color): Create list owned by userupdateList(userId, listId, data): Update list if user has permissiondeleteList(userId, listId): Delete list if user is ownergetListWithItems(userId, listId): Get list if user has accesscreateItem(userId, listId, data): Create item if user can editupdateItem(userId, itemId, data): Update item if user can edit parent listdeleteItem(userId, itemId): Delete item if user can edit parent listtoggleItem(userId, itemId): Toggle item if user can edit parent list
Sharing Actions (lib/sharing-actions.ts)
inviteUserToList(ownerId, listId, email, permission): Create invitationgetInvitations(userId): Get pending invitations for useracceptInvitation(userId, invitationId): Accept invitation and grant accessdeclineInvitation(userId, invitationId): Decline and remove invitationgetCollaborators(userId, listId): Get users with access to listupdateCollaboratorPermission(ownerId, listId, collaboratorId, permission): Change permission levelremoveCollaborator(ownerId, listId, collaboratorId): Revoke access
Presentation Layer
New Pages
/auth/login- Login form/auth/register- Registration form/auth/error- Authentication error display/profile- User profile management/invitations- Pending invitations list
New Components
LoginForm: Email/password login form with validationRegisterForm: Registration form with password requirementsProfileForm: Edit user profile informationPasswordChangeForm: Change password with current password verificationInvitationCard: Display invitation with accept/decline actionsCollaboratorList: Show users with access to a listShareListModal: Invite users to a listPermissionBadge: Display permission level (owner/editor/viewer)ProtectedRoute: Wrapper component for authenticated routes
Modified Components
ListCard: Show sharing indicator if list has collaboratorsListDetailView: Add share button for owners, show permission levelItemForm: Disable editing for viewers
Data Models
New Database Models
User Model
{
id: string (cuid)
email: string (unique)
name: string | null
passwordHash: string
createdAt: DateTime
updatedAt: DateTime
ownedLists: List[] (relation)
collaborations: Collaborator[] (relation)
sentInvitations: Invitation[] (relation)
receivedInvitations: Invitation[] (relation)
}
Collaborator Model (Join table for list sharing)
{
id: string (cuid)
userId: string
listId: string
permission: 'editor' | 'viewer'
createdAt: DateTime
user: User (relation)
list: List (relation)
@@unique([userId, listId])
}
Invitation Model
{
id: string (cuid)
email: string
listId: string
inviterId: string
permission: 'editor' | 'viewer'
status: 'pending' | 'accepted' | 'declined'
createdAt: DateTime
expiresAt: DateTime
list: List (relation)
inviter: User (relation)
}
Modified Database Models
List Model (add owner relationship)
{
id: string (cuid)
title: string
color: string
ownerId: string (new)
createdAt: DateTime
updatedAt: DateTime
owner: User (new relation)
items: Item[]
collaborators: Collaborator[] (new relation)
invitations: Invitation[] (new relation)
}
Item Model (no changes to schema, but queries will check list access)
{
// Existing fields remain the same
// Access control enforced through parent list
}
Session Model
Session Data (stored in JWT)
{
user: {
id: string
email: string
name: string | null
}
expires: string (ISO date)
}
Correctness Properties
A property is a characteristic or behavior that should hold true across all valid executions of a system—essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.
User Registration Properties
Property 1: Registration creates unique user For any valid email and password, registering a new user should create a user account with a unique identifier that differs from all existing user identifiers. Validates: Requirements 1.1
Property 2: Duplicate email rejection For any existing user email, attempting to register with that email should be rejected and return an error. Validates: Requirements 1.2
Property 3: Password hashing on registration For any user registration with a password, the stored password value in the database should be a valid bcrypt hash and should not equal the plain text password. Validates: Requirements 1.3
Property 4: Registration creates session For any successful user registration, a valid session should be created for that user immediately after registration. Validates: Requirements 1.4
Property 5: Password length validation For any password with fewer than 8 characters, registration should be rejected and return a validation error. Validates: Requirements 1.5
Authentication Properties
Property 6: Valid credentials create session For any registered user with known password, submitting correct credentials should authenticate the user and create a valid session. Validates: Requirements 2.1
Property 7: Invalid credentials rejection For any login attempt with incorrect password or non-existent email, authentication should fail and return an error. Validates: Requirements 2.2
Property 8: Session expiration enforcement For any expired session token, attempting to access protected resources should be denied. Validates: Requirements 2.4
Property 9: Logout terminates session For any authenticated user, after logging out, the session token should become invalid and subsequent requests should be denied. Validates: Requirements 2.5
Session Management Properties
Property 10: Secure token generation For any successful login, a session token should be created with sufficient entropy and proper format. Validates: Requirements 3.1
Property 11: Session persistence For any valid session token, re-validating the token should maintain the authenticated state with the same user information. Validates: Requirements 3.2
Property 12: Session duration persistence For any session created, the session should remain valid for the configured duration even after simulated browser close/reopen. Validates: Requirements 3.4
List Ownership Properties
Property 13: List ownership assignment For any user creating a list, the created list should have its ownerId set to the creating user's ID. Validates: Requirements 4.1
Property 14: List access filtering For any user querying for lists, the results should include only lists where the user is the owner or has a collaborator record, and should include all such lists. Validates: Requirements 4.2, 4.4
Property 15: Unauthorized list access denial For any user attempting to access a list they don't own and aren't a collaborator on, the access should be denied and return an error. Validates: Requirements 4.3
Property 16: Account deletion cascades to lists For any user with owned lists, deleting the user account should result in all owned lists being removed from the database. Validates: Requirements 4.5
Invitation Properties
Property 17: Invitation creation with unique ID For any list owner inviting a user by email, an invitation record should be created with a unique identifier and the specified permission level. Validates: Requirements 5.1, 6.1
Property 18: Invitation acceptance grants access For any pending invitation, when the invited user accepts it, a collaborator record should be created granting the user access to the list with the specified permission. Validates: Requirements 5.2
Property 19: Invitation decline removes record For any pending invitation, when the invited user declines it, the invitation should be removed from the database. Validates: Requirements 5.3
Property 20: Collaborator list completeness For any list with collaborators, querying the collaborators should return all users who have access including their permission levels. Validates: Requirements 5.4
Property 21: Access revocation enforcement For any collaborator with access to a list, after the owner revokes access, the user should no longer be able to access the list. Validates: Requirements 5.5
Permission Properties
Property 22: Editor permission allows modifications For any user with editor permission on a list, the user should be able to create, update, and delete items in that list. Validates: Requirements 6.2
Property 23: Viewer permission prevents modifications For any user with viewer permission on a list, attempts to create, update, or delete items should be denied and return an error. Validates: Requirements 6.3, 6.4
Property 24: Permission update enforcement For any collaborator with editor permission, after the owner changes their permission to viewer, item modification attempts should be denied. Validates: Requirements 6.5
Profile Management Properties
Property 25: Profile update persistence For any user updating profile information, the changes should be saved and associated with the correct user account. Validates: Requirements 7.1
Property 26: Password change requires current password For any password change attempt with incorrect current password, the change should be rejected and return an error. Validates: Requirements 7.2
Property 27: Password hashing on change For any successful password change, the new password should be stored as a bcrypt hash and not as plain text. Validates: Requirements 7.3
Property 28: Email uniqueness validation For any attempt to update email to an address already in use by another user, the update should be rejected and return an error. Validates: Requirements 7.4
Property 29: Profile data exposure protection For any user profile query, the returned data should not include the password hash or other sensitive authentication data. Validates: Requirements 7.5
Invitation Notification Properties
Property 30: Invitation visibility For any invitation created for a user's email, querying that user's pending invitations should include the invitation. Validates: Requirements 8.1
Property 31: Invitation details completeness For any pending invitation, the invitation data should include the list name and inviter information. Validates: Requirements 8.2
Property 32: Invitation removal after action For any invitation that is accepted or declined, the invitation should no longer appear in the user's pending invitations list. Validates: Requirements 8.4
Property 33: Invitation timestamp recording For any created invitation, the invitation should have a createdAt timestamp that is set to the time of creation. Validates: Requirements 8.5
Data Isolation Properties
Property 34: Item access through list permission For any user querying items, only items from lists the user has access to should be returned. Validates: Requirements 9.2
Migration Properties
Property 35: Migration data preservation For any list migrated to a user account, all list data including items, metadata, and timestamps should remain unchanged after migration. Validates: Requirements 10.3
Error Handling
Authentication Errors
Invalid Credentials
- Return clear error messages without revealing whether email exists
- Log failed login attempts for security monitoring
- Implement rate limiting to prevent brute force attacks
Session Errors
- Handle expired sessions gracefully with redirect to login
- Clear invalid tokens from client storage
- Provide clear feedback when re-authentication is required
Registration Errors
- Validate email format before database query
- Check password requirements client-side and server-side
- Return specific validation errors for each field
Authorization Errors
Access Denied
- Return 403 Forbidden for unauthorized access attempts
- Log unauthorized access attempts
- Provide user-friendly error messages
Permission Errors
- Check permissions before executing operations
- Return clear error messages indicating required permission level
- Prevent information leakage about resources user can't access
Database Errors
Constraint Violations
- Handle unique constraint violations (duplicate email)
- Manage foreign key constraints properly
- Use transactions for multi-step operations
Connection Failures
- Implement retry logic with exponential backoff
- Provide fallback error messages
- Log database errors for debugging
Invitation Errors
Invalid Invitations
- Validate email format before creating invitation
- Check if user is already a collaborator
- Handle expired invitations gracefully
Invitation Conflicts
- Prevent duplicate invitations to same email for same list
- Handle case where invited email doesn't exist yet
- Manage invitation expiration
Testing Strategy
Unit Testing
The application will use Vitest as the testing framework for unit tests. Unit tests will focus on:
Authentication Layer
- Password hashing and verification
- Token generation and validation
- Credential validation logic
- Session creation and management
Access Control Layer
- Permission checking functions
- Access control filtering logic
- Collaborator management operations
Business Logic
- Invitation creation and acceptance flow
- Profile update operations
- List ownership assignment
- Cascade deletion logic
Utility Functions
- Email validation
- Password strength validation
- Token generation
- Data sanitization
Unit tests will use realistic test data and verify specific examples, edge cases, and error conditions.
Property-Based Testing
The application will use fast-check as the property-based testing library. Property-based tests will verify the universal correctness properties defined in this document.
Configuration Requirements:
- Each property-based test MUST run a minimum of 100 iterations
- Each test MUST be tagged with a comment referencing the correctness property
- Tag format:
// Feature: user-auth-and-sharing, Property {number}: {property_text} - Each correctness property MUST be implemented by a SINGLE property-based test
Property Test Coverage:
- User registration (Properties 1-5)
- Authentication (Properties 6-9)
- Session management (Properties 10-12)
- List ownership (Properties 13-16)
- Invitations (Properties 17-21)
- Permissions (Properties 22-24)
- Profile management (Properties 25-29)
- Invitation notifications (Properties 30-33)
- Data isolation (Property 34)
- Migration (Property 35)
Test Data Generation: Property-based tests will use custom generators for:
- Valid email addresses
- Passwords of various lengths
- User IDs (cuid format)
- Permission levels (owner, editor, viewer)
- Invitation statuses
- Session tokens
- Profile data
Integration Testing
Integration tests will verify:
- End-to-end authentication flows (register → login → access protected resource)
- List sharing workflows (invite → accept → collaborate)
- Permission enforcement across the stack
- Session persistence across requests
- Middleware integration with authentication
Security Testing
Security tests will verify:
- Password hashing strength
- Session token security
- SQL injection prevention
- XSS prevention in user inputs
- CSRF protection
- Rate limiting effectiveness
Performance Considerations
Authentication Performance
- Cache session validation results
- Use efficient bcrypt work factor (10-12 rounds)
- Implement connection pooling for database
- Index user email field for fast lookups
Access Control Performance
- Index foreign keys (userId, listId in Collaborator table)
- Cache permission checks for frequently accessed lists
- Use database-level filtering for access control
- Implement efficient JOIN queries for list retrieval
Session Management
- Use stateless JWT tokens to avoid database lookups
- Set appropriate token expiration times
- Implement token refresh mechanism if needed
- Use secure, httpOnly cookies for token storage
Security Considerations
Password Security
- Use bcrypt with appropriate work factor (10-12 rounds)
- Enforce minimum password length (8 characters)
- Consider additional password requirements (complexity)
- Never log or expose passwords in plain text
- Implement password reset functionality securely
Session Security
- Use cryptographically secure random token generation
- Set httpOnly and secure flags on session cookies
- Implement CSRF protection
- Use short session expiration times
- Implement session invalidation on logout
Access Control Security
- Enforce access control at database query level
- Never trust client-side permission checks
- Validate user identity on every request
- Use parameterized queries to prevent SQL injection
- Implement rate limiting on sensitive operations
Data Protection
- Encrypt sensitive data at rest
- Use HTTPS for all communications
- Implement proper CORS policies
- Sanitize all user inputs
- Follow principle of least privilege
Invitation Security
- Implement invitation expiration
- Validate email addresses
- Prevent invitation spam
- Rate limit invitation creation
- Verify inviter has permission to share
Migration Strategy
Data Migration
Phase 1: Schema Updates
- Add User table to database
- Add Collaborator table
- Add Invitation table
- Add ownerId field to List table (nullable initially)
Phase 2: Migration Script
- Create a default "system" user for existing lists
- Associate all existing lists with system user
- Provide UI for users to claim lists by creating accounts
- Make ownerId non-nullable after migration
Phase 3: Deployment
- Deploy authentication system
- Run migration script
- Enable authentication requirements
- Monitor for issues
Backward Compatibility
- Maintain existing API endpoints during transition
- Provide grace period for users to create accounts
- Preserve all existing list and item data
- Ensure PWA functionality continues to work
Deployment Considerations
Environment Variables
DATABASE_URL=mysql://...
NEXTAUTH_URL=https://yourdomain.com
NEXTAUTH_SECRET=<generated-secret>
JWT_SECRET=<generated-secret>
Database Migrations
- Run Prisma migrations:
npx prisma migrate dev - Generate Prisma client:
npx prisma generate - Run data migration script if needed
- Verify schema changes in production
Monitoring
- Track failed login attempts
- Monitor session creation/expiration
- Log unauthorized access attempts
- Track invitation acceptance rates
- Monitor database query performance
Future Enhancements
Authentication Features
- OAuth providers (Google, GitHub, etc.)
- Two-factor authentication
- Password reset via email
- Email verification
- Remember me functionality
- Account recovery options
Collaboration Features
- Real-time collaboration indicators
- Activity feed for shared lists
- Comments on items
- @mentions in notes
- Notification system for list updates
- Team/organization support
Permission Enhancements
- Custom permission levels
- Granular item-level permissions
- Temporary access grants
- Permission templates
- Audit log for permission changes