Fastify TypeScript backend API implementing secure authentication with JWT-based access/refresh token rotation, OAuth2 integration, email services, and user profile management.
- JWT-based authentication (access + refresh tokens)
- Refresh token rotation with automatic revocation
- HTTP-only, Secure, SameSite cookies for refresh tokens
- OAuth2 integration (Google, GitHub, GitLab, Discord)
- Email verification system with Resend
- Password reset flow with secure tokens
- Rate limiting on sensitive endpoints
- bcryptjs password hashing
- Fastify for high-performance HTTP handling
- ️ MongoDB with native driver (no ODM)
- Connection pooling (up to 120 concurrent operations)
- Database indexes for optimized queries
- Zod schema validation on all inputs
- CORS configured for production/development
- Multipart form data for file uploads
- ️ Helmet for security headers
- User profile CRUD operations
- Profile image upload (JPEG/PNG)
- Email notifications via Resend
- Node.js with TypeScript
- Fastify - Fast and low overhead web framework
- MongoDB - NoSQL database with native driver (no ODM)
- Zod - TypeScript-first schema validation
- bcryptjs - Password hashing
- jsonwebtoken - JWT generation and verification
- Resend - Email delivery service
- Fastify plugins: helmet, cors, cookie, multipart, static, sensible
The application is organized into modules, each representing a distinct domain:
- auth module: Authentication, registration, OAuth, email verification, password reset
- user module: User profiles, image uploads, account management
Each module contains:
- Routes: Fastify route definitions with hooks and middleware
- Controller: HTTP request/response handling
- Service: Business logic and database operations
- Schema: Zod validation schemas
- Model: TypeScript types and interfaces
Services and controllers are wired together using a DI Plugin:
- Services are singletons sharing the same DB connection
- Controllers receive dependencies via constructor injection
- All wiring happens in the composition root
- Uses MongoDB native driver directly (no ODM)
- Connection pooling with max 120 concurrent operations
- Indexes created on startup for optimal query performance
- Service layer handles all DB operations
- Controllers only interact with DB for transactions
Fastify decorators provide reusable authentication guards:
verifyAccessTokenHeader: Validates JWT from Authorization headerverifyRefreshTokenCookie: Validates refresh token from cookiesverifySocialProfileTokenCookie: Validates OAuth profile tokensverifyResetPasswordCookie: Validates password reset tokensisAuthenticated: Checks if user has valid authenticationactionForbiddenToAuthenticatedUser: Blocks authenticated users from auth routes (not actually needed)verifyImageUpload: Validates uploaded image files
- Node.js v18 or higher
- MongoDB running locally or connection string
- OAuth2 credentials from providers (Google, GitHub, GitLab, Discord)
- Resend API key for email sending
cd server
npm installStart the development server with HTTPS:
npm run devServer runs on https://localhost:3000 (HTTPS with self-signed cert)
Build and run:
npm run build
npm start| Command | Description |
|---|---|
npm run dev |
Start development server with hot reload |
npm run build |
Compile TypeScript to JavaScript |
npm start |
Run production build |
Create .env.development and .env.production files:
# Database
MONGO_URI=mongodb://localhost:27017/authapp
DATABASE_NAME=authapp
# Server
PORT=3000
NODE_ENV=development
SERVER_URI=https://localhost:3000
CLIENT_URI=http://localhost:5173
# JWT Secrets (generate strong random strings)
ACCESS_TOKEN_SECRET=your-access-token-secret-min-32-chars
REFRESH_TOKEN_SECRET=your-refresh-token-secret-min-32-chars
EMAIL_SECRET=your-email-verification-secret
RESET_PASS_COOKIE_SECRET=your-reset-password-secret
# JWT Expiration
ACCESS_TOKEN_EXPIRATION_TIME=3m
REFRESH_TOKEN_EXPIRATION_TIME=30d
EMAIL_TOKEN_EXPIRATION_TIME=1h
RESET_PASS_COOKIE_EXPIRATION_TIME=30m
# Cookie Names
COOKIE_NAME=refreshToken
COOKIE_NAME_SOCIAL_PROFILE=socialProfile
RESET_PASS_COOKIE_NAME=resetPassword
# Password Hashing
SALT_SIZE=10
# Email Service (Resend)
RESEND_API_KEY=your-resend-api-key
EMAIL_FROM=noreply@yourdomain.com
# OAuth2 - Google
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# OAuth2 - GitHub
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
# OAuth2 - GitLab
GITLAB_CLIENT_ID=your-gitlab-client-id
GITLAB_CLIENT_SECRET=your-gitlab-client-secret
# OAuth2 - Discord
DISCORD_CLIENT_ID=your-discord-client-id
DISCORD_CLIENT_SECRET=your-discord-client-secret- Registration: Password hashed with bcryptjs, verification email sent via Resend
- Email Verification: User clicks link, JWT verified, user marked as verified
- Login: Access token (3min) in response body, refresh token (30 days) in HTTP-only, secure cookie
- Client sends authorization code to server
- Server exchanges code for access token with provider
- Server fetches user profile from provider
- Create or login user
- Return access token (body) and refresh token (cookie)
- Client detects expired access token (401)
- Calls refresh endpoint with refresh token cookie
- Server verifies token (JWT + DB check)
- Generates new token pair with rotation
- Old refresh token revoked and auto-deleted via MongoDB TTL index
- Returns new tokens
- Server marks refresh token as revoked in DB
- Sets
deleteAtfield for auto-deletion - Clears all authentication cookies
- ✅ Access tokens in memory on client (3 minutes lifetime)
- ✅ Refresh tokens in HTTP-only, Secure cookies (30 days lifetime)
- ✅ Refresh token rotation (old tokens revoked immediately)
- ✅ Device tracking (IP + user agent stored with tokens)
- ✅ Auto-deletion of revoked tokens via MongoDB TTL indexes
- ✅ bcryptjs hashing with salt rounds
- ✅ Password requirements enforced via Zod validation
- ✅ Reset tokens in HTTP-only cookies
- ✅ CORS configured for specific origins
- ✅ Helmet security headers
- ✅ Input validation with Zod
- ✅ TypeScript for type safety
- ✅ File upload validation (type, size)
Development server uses self-signed SSL certificates (cert.pem, key.pem) for local HTTPS. These files are git-ignored.
Generate new certificates:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodesServices return standardized responses:
type ServiceResponse = {
success: boolean;
data?: any;
customError?: string;
};MIT
Built with Fastify, TypeScript, and MongoDB