- Project Overview
- Architecture
- API Reference
- Onboarding Guide
- Type System
- Error Handling
- OpenAPI Spec
- Lint: Code style and formatting checks (auto-run on every PR).
- Tests: Automated test suite status.
- Docs: Documentation and onboarding coverage.
- Vercel: Production deployment status (auto-deploys on main).
- Docs/Onboarding: Checks that onboarding script and docs are up to date.
.cursor/rules/. This README is for usage, onboarding, and high-level documentation only.
This project is a user authentication system built with Next.js, designed to be LLM- and onboarding-friendly. It features automated documentation, CI/CD, and a modular, type-safe architecture. For detailed flows, usage, and technical docs, see the docs/ directory.
erDiagram
USER {
uuid id
varchar email
timestamptz createdAt
}
AUTH_METHOD {
uuid id
uuid userId
varchar type
text credential
timestamptz verifiedAt
timestamptz expiresAt
timestamptz createdAt
}
USER ||--o{ AUTH_METHOD : has
The register function handles user registration. It validates the input data, checks if the user already exists, creates a new user, and sets the password.
import { register } from "@/app/(auth)/actions";
const formData = new FormData();
formData.append("email", "[email protected]");
formData.append("password", "password123");
const state = await register({ status: "idle" }, formData);The login function handles user login. It validates the input data, checks if the user exists, validates the password, and creates a session.
import { login } from "@/app/(auth)/actions";
const formData = new FormData();
formData.append("email", "[email protected]");
formData.append("password", "password123");
const state = await login({ status: "idle" }, formData);The resetPassword function handles password reset. It validates the token, hashes the new password, and updates the password.
import { resetPassword } from "@/app/(auth)/lib/db/queries";
const token = "reset-token";
const newPassword = "newpassword123";
await resetPassword(token, newPassword);The createSession, renewSession, and validateSession functions handle session management.
import { createSession, renewSession, validateSession } from "@/app/(auth)/lib/db/queries";
const userId = "user-id";
await createSession(userId);
const sessionToken = "session-token";
await renewSession(sessionToken);
const isValid = await validateSession(sessionToken);UUIDs are generated as credentials (tokens) for each authentication method in the createAuthMethod function in app/(auth)/lib/db/queries.ts. If no credential is provided, a UUID is generated.
The following functions use createAuthMethod to generate tokens:
createSessioninapp/(auth)/lib/db/queries.tsgenerates a session token.resetPasswordinapp/(auth)/lib/db/queries.tsgenerates a reset password token.sendVerificationEmailinapp/(auth)/lib/email.tsgenerates a verification token.sendResetPasswordEmailinapp/(auth)/lib/email.tsgenerates a reset password token.createPasskeyinapp/(auth)/lib/db/queries.tsgenerates a passkey token.createPasswordinapp/(auth)/lib/db/queries.tsis the only method where a token is not generated. Instead, a hashed password is stored.
For each authentication method, the expiresAt field indicates when the token will expire, and the verifiedAt field indicates when the token was verified.
sequenceDiagram
participant User
participant Client
participant Server
User->>Client: Fill registration form
Client->>Server: Submit registration form
Server->>Server: Validate data
Server->>Server: Check if user exists
Server->>Server: Create user
Server->>Server: Set password
Server->>Client: Return success
Client->>User: Show success message
This project uses kysely-codegen to generate TypeScript types from the live database schema.
- Script:
npm run db:typegen - Output:
app/(auth)/lib/types/database.generated.ts - When to run: After every migration or schema change.
This ensures your TypeScript types stay in sync with your database schema. Never edit the generated file by hand.
sequenceDiagram
participant User
participant Client
participant Server
User->>Client: Fill login form
Client->>Server: Submit login form
Server->>Server: Validate data
Server->>Server: Check if user exists
Server->>Server: Validate password
Server->>Server: Create session
Server->>Client: Return success
Client->>User: Show success message
sequenceDiagram
participant User
participant Client
participant Server
User->>Client: Request password reset
Client->>Server: Submit reset request
Server->>Server: Generate reset token
Server->>User: Send reset token
User->>Client: Fill reset form with token
Client->>Server: Submit reset form
Server->>Server: Validate token
Server->>Server: Hash new password
Server->>Server: Update password
Server->>Client: Return success
Client->>User: Show success message
sequenceDiagram
participant User
participant Client
participant Server
User->>Client: Login
Client->>Server: Submit login form
Server->>Server: Validate data
Server->>Server: Check if user exists
Server->>Server: Validate password
Server->>Server: Create session
Server->>Client: Return session token
Client->>User: Store session token
User->>Client: Access protected resource
Client->>Server: Validate session token
Server->>Server: Check if session is valid
Server->>Client: Return success
Client->>User: Allow access
sequenceDiagram
participant User
participant Client
participant Server
User->>Client: Request passkey authentication
Client->>Server: Generate authentication options
Server->>Client: Return authentication options
Client->>User: Start authentication
User->>Client: Provide authentication response
Client->>Server: Verify authentication response
Server->>Server: Validate response
Server->>Client: Return success
Client->>User: Show success message
The passkey user authentication flow involves generating options, starting authentication or registration, and verifying responses. This flow aligns with other documented authentication methods.
This project includes an OpenAPI specification file (openapi.yaml) in the root directory. Whenever you add, change, or remove any API endpoints, you must update openapi.yaml to reflect those changes.
- The OpenAPI file documents all public API endpoints, their request/response formats, and error codes.
- Code reviews should verify that
openapi.yamlis kept up to date with the implementation. - Keeping this file current ensures accurate API documentation and helps with client/server integration, testing, and onboarding.
This project uses a centralized error registry (app/(auth)/lib/errors.ts) for consistent error handling across all API endpoints.
- Standardized error codes and messages for all error scenarios
- Proper HTTP status codes mapped to each error type
- Structured error responses with timestamps and additional details
- Type safety with TypeScript and Zod integration
- Automatic error handling for validation errors and unknown exceptions
For detailed usage instructions and examples, see docs/error-handling.md.
Key benefits:
- Consistent error responses across all endpoints
- Easy client-side error handling with machine-readable codes
- Simplified debugging with structured error details
- Type-safe error creation and handling
This project uses a centralized type system (app/(auth)/lib/types/index.ts) that serves as the single source of truth for all shared types.
- Database types from Kysely (User, AuthMethod, etc.)
- API request/response types for all endpoints
- Zod schemas for validation and type inference
- Error types integrated with the error registry
- Utility types for common patterns (pagination, API responses)
- Type guards for runtime type checking
- Constants for configuration and auth method types
For detailed documentation and usage examples, see docs/type-system.md.
Key benefits:
- Single source of truth for all types across the application
- Type safety from database to API responses
- Consistent validation with Zod schemas
- Easy refactoring with centralized type changes
- Better IDE support and LLM-friendly documentation
This project uses a CHANGELOG.md to track all notable changes. Please follow these steps:
- Before committing any new feature, fix, or breaking change, add an entry to the
Unreleasedsection ofCHANGELOG.md. - Use the Keep a Changelog format for consistency.
- When preparing a release, move entries from
Unreleasedto a new version section with the release date. - Keeping the changelog up to date helps with transparency, onboarding, and release management.