A robust authentication microservice built with Node.js, TypeScript, and PostgreSQL for the MERN stack ecosystem. This service provides secure user registration, authentication, and token management with JWT-based access and refresh tokens.
- User Registration - Secure user account creation with validation
- User Login - Email and password authentication
- JWT Authentication - Dual token system (access + refresh tokens)
- Token Refresh - Automatic token refresh mechanism
- User Logout - Secure logout with token invalidation
- Self Endpoint - Get current user information
- Password Security - Bcrypt password hashing with salt rounds
- Cookie-based Sessions - HTTP-only, SameSite cookies for secure token storage
- Token Revocation - Database-backed refresh token management
- Database Integration - PostgreSQL with TypeORM and migrations
- Input Validation - Express-validator for request validation
- Logging - Winston logger for comprehensive logging and audit trails
- CORS Support - Configurable CORS for multiple domains
- TypeScript - Full TypeScript support with strict typing
- Testing - Jest testing framework setup
- Code Quality - ESLint, Prettier, and Husky pre-commit hooks
- RSA Key Generation - Scripts for generating JWT signing keys
- Runtime: Node.js
- Language: TypeScript
- Framework: Express.js
- Database: PostgreSQL
- ORM: TypeORM
- Authentication: JWT (jsonwebtoken)
- Password Hashing: bcrypt
- Validation: express-validator
- Logging: Winston
- Testing: Jest
- Code Quality: ESLint, Prettier, Husky
- Node.js (v18 or higher)
- PostgreSQL (v12 or higher)
- npm or yarn
git clone <repository-url>
cd auth-servicenpm installCreate environment files for different environments:
# Development environment
cp .env.example .env.dev
# Production environment
cp .env.example .env.prodConfigure your .env.dev file:
NODE_ENV=dev
PORT=5501
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=your_db_username
DB_PASSWORD=your_db_password
DB_NAME=auth_service_dev
REFRESH_JWT_SECRET=your_refresh_jwt_secret_min_32_chars
CLIENT_UI_DOMAIN=http://localhost:5173| Variable | Description | Example |
|---|---|---|
| NODE_ENV | Environment mode | dev, prod, test |
| PORT | Port number for the server | 5501 |
| DB_HOST | PostgreSQL host | localhost |
| DB_PORT | PostgreSQL port | 5432 |
| DB_USERNAME | PostgreSQL username | postgres |
| DB_PASSWORD | PostgreSQL password | your_password |
| DB_NAME | Database name | auth_service_dev |
| REFRESH_JWT_SECRET | Secret for signing refresh tokens (min 32 chars) | your_secure_secret_key_here |
| CLIENT_UI_DOMAIN | Frontend application URL for CORS | http://localhost:5173 |
Create the database:
npm run db:createRun database migrations:
npm run migration:runGenerate RSA key pair for signing access tokens:
node scripts/generateKeys.mjsThis will create:
certs/private.pem- Private key for signing access tokenscerts/public.pem- Public key for verifying access tokens
You can also convert the public key to JWK format:
node scripts/convertPemToJwk.mjsDevelopment mode with hot reload:
npm run devProduction mode:
npm startThe service will be available at http://localhost:3000
- User submits credentials (email/password)
- Server validates input and credentials
- Server generates two tokens:
- Access Token: Short-lived (1h), signed with RS256, contains user ID and role
- Refresh Token: Long-lived (30d), signed with HS256, stored in database
- Both tokens are sent as HTTP-only cookies
- Client uses access token for authenticated requests
- When access token expires, client calls
/auth/refreshwith refresh token - Server validates refresh token and checks if it's revoked
- Server generates new access and refresh tokens
- Old refresh token is deleted from database
- New tokens are sent as cookies
- Client calls
/auth/logoutwith both tokens - Server deletes refresh token from database
- Server clears both cookies
- User is logged out
Register a new user account.
Request Body:
{
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"password": "securePassword123"
}Response:
{
"message": "User registered successfully",
"userId": 1
}Cookies Set:
access_token- JWT access token (1 hour expiry)refresh_token- JWT refresh token (30 days expiry)
Authenticate an existing user.
Request Body:
{
"email": "john.doe@example.com",
"password": "securePassword123"
}Response:
{
"message": "User logged in successfully",
"userId": 1
}Cookies Set:
access_token- JWT access token (1 hour expiry)refresh_token- JWT refresh token (30 days expiry)
Get current authenticated user details.
Headers:
- Requires valid
access_tokencookie
Response:
{
"message": "User details fetched successfully",
"user": {
"id": 1,
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"role": "customer",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
}Refresh access token using refresh token.
Headers:
- Requires valid
refresh_tokencookie
Response:
{
"message": "Token refreshed successfully",
"userId": 1
}Cookies Set:
access_token- New JWT access token (1 hour expiry)refresh_token- New JWT refresh token (30 days expiry)
Note: Old refresh token is automatically deleted from the database.
Logout the current user and invalidate tokens.
Headers:
- Requires valid
access_tokencookie - Requires valid
refresh_tokencookie
Response:
{
"message": "User logged out successfully"
}Cookies Cleared:
access_tokenrefresh_token
Note: Refresh token is deleted from the database.
| Column | Type | Description |
|---|---|---|
| id | SERIAL PRIMARY KEY | Unique user identifier |
| firstName | VARCHAR(255) | User's first name |
| lastName | VARCHAR(255) | User's last name |
| VARCHAR(255) UNIQUE | User's email address | |
| password | VARCHAR(255) | Hashed password (bcrypt) |
| role | VARCHAR(255) | User role (default: 'customer') |
| created_at | TIMESTAMP | Account creation timestamp |
| updated_at | TIMESTAMP | Last update timestamp |
| Column | Type | Description |
|---|---|---|
| id | SERIAL PRIMARY KEY | Unique token identifier |
| user_id | INTEGER | Foreign key to users table |
| expires_at | TIMESTAMP | Token expiration time |
| created_at | TIMESTAMP | Token creation timestamp |
| updated_at | TIMESTAMP | Last update timestamp |
Run tests in watch mode:
npm testRun tests once:
npm run test -- --watchAll=false| Script | Description |
|---|---|
npm run dev |
Start development server with hot reload |
npm start |
Start production server |
npm test |
Run tests in watch mode |
npm run format:fix |
Format code with Prettier |
npm run format:check |
Check code formatting |
npm run lint:fix |
Fix ESLint errors |
npm run lint:check |
Check for linting errors |
npm run migration:generate |
Generate new migration |
npm run migration:run |
Run pending migrations |
npm run migration:revert |
Revert last migration |
npm run db:create |
Create database |
npm run db:drop |
Drop database |
src/
βββ config/ # Configuration files
β βββ data-source.ts # TypeORM data source
β βββ index.ts # Environment configuration
β βββ logger.ts # Winston logger setup
βββ controller/ # Request handlers
β βββ AuthController.ts
βββ entity/ # Database entities
β βββ User.ts
β βββ RefreshToken.ts
βββ middleware/ # Express middleware
β βββ authenticate.ts # Access token validation
β βββ validateRefreshToken.ts # Refresh token validation
β βββ parseRefreshToken.ts # Refresh token parser
βββ migrations/ # Database migrations
βββ routes/ # API routes
β βββ auth.ts
βββ service/ # Business logic
β βββ TokenService.ts # JWT token generation & management
β βββ UserService.ts # User CRUD operations
β βββ CredentialService.ts # Password hashing & comparison
βββ types/ # TypeScript type definitions
β βββ index.ts
βββ validators/ # Input validation
β βββ register-validators.ts
β βββ login-validators.ts
βββ app.ts # Express app configuration
βββ server.ts # Server entry point
- Password Hashing: Bcrypt with configurable salt rounds for secure password storage
- JWT Tokens:
- Access tokens signed with RS256 (RSA asymmetric encryption)
- Refresh tokens signed with HS256 (HMAC symmetric encryption)
- Short-lived access tokens (1 hour) for security
- Long-lived refresh tokens (30 days) stored in database
- Token Revocation: Refresh tokens can be invalidated and are deleted on logout
- HTTP-Only Cookies: Prevents XSS attacks by making tokens inaccessible to JavaScript
- SameSite Cookies: Protection against CSRF attacks
- CORS Configuration: Controlled cross-origin requests with configurable domains
- Input Validation: Express-validator for comprehensive request validation
- SQL Injection Protection: TypeORM parameterized queries
- Environment Variables: Sensitive data protection with dotenv
- Error Handling: Centralized error handling with http-errors
- Logging: Winston logger for security audit trails
Build and run with Docker:
# Development
docker build -f docker/dev/Dockerfile -t auth-service:dev .
# Production
docker build -f docker/prod/Dockerfile -t auth-service:prod .Ensure all required environment variables are set in your production environment:
- Database connection details
- JWT secrets
- CORS domains
- Port configuration
Make sure PostgreSQL is running and the database exists:
# Check if PostgreSQL is running
pg_isready
# Create the database if it doesn't exist
npm run db:createIf you get errors about missing private/public keys:
node scripts/generateKeys.mjsChange the PORT in your .env.dev file or kill the process using the port:
# Find process using port 3000
lsof -ti:3000
# Kill the process
kill -9 $(lsof -ti:3000)If migrations fail, try reverting and running again:
npm run migration:revert
npm run migration:run- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow the existing code style
- Run
npm run format:fixbefore committing - Run
npm run lint:fixto fix linting errors - All commits are automatically checked with Husky pre-commit hooks
Ashutosh S
- Email: ashudev0987@gmail.com
If you encounter any issues or have questions, please:
- Check the existing issues
- Create a new issue with detailed information
- Contact the maintainer
# Development
npm run dev # Start dev server with hot reload
npm run format:fix # Format code with Prettier
npm run lint:fix # Fix linting errors
# Database
npm run db:create # Create database
npm run db:drop # Drop database
npm run migration:generate # Generate migration from entities
npm run migration:run # Run pending migrations
npm run migration:revert # Revert last migration
# Testing
npm test # Run tests in watch mode
npm run test -- --watchAll=false # Run tests once
# Production
npm start # Start production server- Development:
http://localhost:5501/api - All auth endpoints are prefixed with
/auth
- New users are assigned the
customerrole by default - Roles can be extended in the User entity
Note: This is a microservice designed to work as part of a larger MERN stack application. Make sure to configure the CORS domains and other settings according to your frontend applications.