A comprehensive NestJS-based authentication microservice for the Channeling Service platform. This service provides secure user authentication with support for multiple login methods including email/password, mobile OTP, and OAuth (Google & Azure AD).
-
Multi-method Authentication
- Email/Password login (Web)
- Phone Number + OTP login (Mobile)
- Google OAuth 2.0
- Azure AD OAuth
-
Role-Based Access Control (RBAC)
- 6 User Roles:
super_admin,admin,hospital,doctor,nurse,patient - Granular permissions based on role hierarchy
- 6 User Roles:
-
Security Features
- JWT token-based authentication
- Password hashing with bcrypt
- Rate limiting on sensitive endpoints
- Helmet for HTTP security headers
- CORS enabled
- Input validation with class-validator
-
OTP Management
- Configurable expiry time
- Rate limiting with cooldown period
- Secure email delivery
- Attempt limiting
-
API Documentation
- Interactive Swagger/OpenAPI docs
- Comprehensive endpoint descriptions
- Request/Response examples
- Node.js (v18 or higher)
- npm or yarn
- Supabase account and project
- Gmail account (for OTP emails) with App Password
- Google OAuth credentials (optional)
- Azure AD OAuth credentials (optional)
git clone <repository-url>
cd user-servicenpm installCopy .env.example to .env:
copy .env.example .envUpdate the .env file with your credentials:
# Application
PORT=3000
NODE_ENV=development
FRONTEND_URL=http://localhost:3001
# Supabase
SUPABASE_URL=your-supabase-url
SUPABASE_ANON_KEY=your-supabase-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-supabase-service-role-key
# JWT Configuration
JWT_SECRET=your-super-secret-key-change-in-production-minimum-32-characters-long
JWT_EXPIRATION=24h
# Refresh token configuration (optional - recommended)
JWT_REFRESH_SECRET=another-super-secret-key
JWT_REFRESH_EXPIRATION=7d
**Integration guide:** See `docs/AUTH-INTEGRATION.md` for a step-by-step guide on endpoints, web & mobile integration, CSRF handling, code examples for Next.js and React, and deployment checklist.
**Local token verification utility:**
A small helper script is available to decode and verify JWTs locally using your `.env` values:
npm run verify:jwt -- --token --use-env
npm run verify:jwt -- --token --secret "your-secret-here"
This utility is intended for local development and debugging only. Do NOT expose token verification endpoints in production.
# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_CALLBACK_URL=http://localhost:3000/api/auth/google/callback
# Azure AD OAuth
AZURE_AD_CLIENT_ID=your-azure-client-id
AZURE_AD_TENANT_ID=your-azure-tenant-id
AZURE_AD_CLIENT_SECRET=your-azure-client-secret
AZURE_AD_CALLBACK_URL=http://localhost:3000/api/auth/azure/callback
# Email Configuration
EMAIL_USER=your-email@gmail.com
EMAIL_PASS=your-gmail-app-password
EMAIL_FROM=noreply@channelingservice.com
# OTP Configuration
OTP_EXPIRY_MINUTES=5
OTP_COOLDOWN_SECONDS=60
Create the following tables in your Supabase project:
CREATE TABLE users (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
phone_number TEXT UNIQUE NOT NULL,
role TEXT NOT NULL CHECK (role IN ('super_admin', 'admin', 'hospital', 'doctor', 'nurse', 'patient')),
age INTEGER,
gender TEXT CHECK (gender IN ('male', 'female', 'other')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create indexes for better performance
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_phone ON users(phone_number);
CREATE INDEX idx_users_role ON users(role);
### Running DB migrations locally or on your DB
This project includes a simple migration runner that applies SQL files placed in `db/migrations/` (ordered by filename) and records applied migrations in a `migrations` table.
Quick steps:
1. Ensure you have a Postgres connection string available as `DATABASE_URL` in your environment (e.g., `postgres://user:pass@host:5432/dbname`).
2. Run the migrations with the npm script:
```bash
DATABASE_URL="postgres://user:pass@host:5432/dbname" npm run migrateIf you don't have direct DB access (e.g., you use Supabase), you can open each .sql file in db/migrations/ and run them in the Supabase SQL Editor.
Note: The migration runner will create and use a migrations table to avoid reapplying the same file.
#### 2. OTPs Table
```sql
CREATE TABLE otps (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
otp TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
attempts INTEGER DEFAULT 0
);
-- Create index for email lookups
CREATE INDEX idx_otps_email ON otps(email);
#### 3. Refresh Tokens Table
```sql
CREATE TABLE refresh_tokens (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
revoked BOOLEAN DEFAULT FALSE
);
CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens(user_id);
-- Auto-delete expired OTPs (optional but recommended) CREATE OR REPLACE FUNCTION delete_expired_otps() RETURNS void AS $$ BEGIN DELETE FROM otps WHERE expires_at < NOW(); END; $$ LANGUAGE plpgsql;
-- Schedule to run every 5 minutes SELECT cron.schedule('delete-expired-otps', '*/5 * * * *', 'SELECT delete_expired_otps()');
#### 3. Enable Row Level Security (RLS)
For better security, enable RLS on your tables:
```sql
-- Enable RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE otps ENABLE ROW LEVEL SECURITY;
-- Create policies (customize based on your needs)
-- Allow service role to do everything
CREATE POLICY "Service role can do everything on users" ON users
FOR ALL USING (true);
CREATE POLICY "Service role can do everything on otps" ON otps
FOR ALL USING (true);
npm run start:devThe application will start on http://localhost:3000
# Build the application
npm run build
# Start production server
npm run start:prodOnce the application is running, access the interactive Swagger documentation at:
http://localhost:3000/api/docs
- POST
/api/auth/register - Public endpoint
- Body:
{ first_name, last_name, email, password, confirm_password, phone_number, role, age?, gender? } - Note: Cannot register with
adminorsuper_adminroles
- POST
/api/auth/login - Public endpoint
- Body:
{ email, password } - Returns:
{ message, user, token }
- POST
/api/auth/send-otp - Public endpoint
- Body:
{ phone_number } - Sends OTP to user's registered email
- POST
/api/auth/verify-otp - Public endpoint
- Body:
{ phone_number, otp } - Returns:
{ message, user, token }
-
GET
/api/auth/google -
Initiates Google OAuth flow
-
GET
/api/auth/google/callback -
Google OAuth callback
-
GET
/api/auth/azure -
Initiates Azure AD OAuth flow
-
GET
/api/auth/azure/callback -
Azure AD OAuth callback
All user endpoints require JWT authentication via Authorization: Bearer <token> header.
- GET
/api/users/me - Returns current user's profile
- GET
/api/users - Requires:
adminorsuper_adminrole - SuperAdmin sees: admin, hospital, doctor, nurse, patient
- Admin sees: hospital, doctor, nurse, patient
- GET
/api/users/role/:role - Requires:
adminorsuper_adminrole - Returns users with specified role (respecting RBAC)
- GET
/api/users/statistics - Requires:
adminorsuper_adminrole - Returns user count by role
super_admin (highest privileges)
ββ Can view: admin, hospital, doctor, nurse, patient
ββ Can manage system-wide settings
admin
ββ Can view: hospital, doctor, nurse, patient
ββ Can manage organizational settings
hospital
ββ Can view: Own profile
ββ Can manage appointments
doctor
ββ Can view: Own profile
ββ Can manage schedules and patients
nurse
ββ Can view: Own profile
ββ Can assist with patient care
patient (lowest privileges)
ββ Can view: Own profile
ββ Can book appointments
To protect an endpoint with role-based access:
@Get('admin-only')
@Roles(Role.Admin, Role.SuperAdmin)
async adminOnlyEndpoint() {
// Only accessible by admin and super_admin
}- JWT Authentication: All protected endpoints require valid JWT tokens
- Password Hashing: Passwords are hashed using bcrypt (salt rounds: 10)
- Rate Limiting: Sensitive endpoints have rate limits
- Login: 5 attempts/minute
- Send OTP: 3 attempts/minute
- Verify OTP: 5 attempts/minute
- Input Validation: All inputs are validated using class-validator
- CORS: Configured to accept requests from trusted origins
- Helmet: Security headers are automatically set
- OTP Security:
- 5-minute expiration
- 60-second cooldown between requests
- Maximum 5 verification attempts
-
Environment Variables:
- Use strong, randomly generated JWT secrets (min 32 characters)
- Never commit
.envfile to version control - Use environment-specific configurations
-
HTTPS:
- Always use HTTPS in production
- Configure proper SSL certificates
-
Database:
- Enable Row Level Security (RLS) on Supabase
- Use connection pooling
- Regular backups
-
Monitoring:
- Implement logging (Winston, Pino)
- Set up error tracking (Sentry)
- Monitor API performance
- Enable 2-Factor Authentication on your Gmail account
- Generate an App Password:
- Go to Google Account β Security
- Select "2-Step Verification"
- At the bottom, select "App passwords"
- Generate a password for "Mail"
- Use this app password in
EMAIL_PASSenvironment variable
To use a different email provider, modify src/mail/mail.service.ts:
this.transporter = nodemailer.createTransport({
host: 'your-smtp-host.com',
port: 587,
secure: false,
auth: {
user: 'your-email@domain.com',
pass: 'your-password',
},
});# Unit tests
npm run test
# E2E tests
npm run test:e2e
# Test coverage
npm run test:covsrc/
βββ auth/ # Authentication module
β βββ controllers/ # Auth endpoints
β βββ services/ # Auth business logic
β βββ strategies/ # Passport strategies (JWT, Google, Azure)
β βββ guards/ # Auth guards (JWT, Roles, OAuth)
β βββ dto/ # Data Transfer Objects
β βββ auth.module.ts
β
βββ users/ # User management module
β βββ controllers/ # User endpoints
β βββ services/ # User business logic
β βββ users.module.ts
β
βββ mail/ # Email service module
β βββ mail.service.ts # Email sending logic
β βββ mail.module.ts
β
βββ database/ # Database module
β βββ database.module.ts
β βββ supabase.provider.ts # Supabase client
β
βββ common/ # Shared utilities
β βββ decorators/ # Custom decorators
β βββ enums/ # Enums (roles, etc.)
β βββ interfaces/ # TypeScript interfaces
β
βββ config/ # Configuration
β βββ configuration.ts # Environment config
β
βββ app.module.ts # Root module
βββ main.ts # Application entry point
-
"Cannot find module" errors
- Run
npm installto ensure all dependencies are installed - Delete
node_modulesandpackage-lock.json, then reinstall
- Run
-
Supabase connection errors
- Verify your Supabase URL and keys in
.env - Check if your IP is allowed in Supabase settings
- Ensure tables are created correctly
- Verify your Supabase URL and keys in
-
Email not sending
- Verify Gmail App Password is correct
- Check if 2FA is enabled on Gmail
- Review email service logs in console
-
JWT errors
- Ensure JWT_SECRET is set and matches across services
- Check token expiration time
- Verify Bearer token format in Authorization header
MIT
For issues and questions:
- Create an issue on GitHub
- Contact the development team
- Check the Swagger documentation at
/api/docs
- Email verification on registration
- Password reset functionality
- Multi-factor authentication (MFA)
- Session management
- User profile image upload
- Audit logging
- WebSocket support for real-time notifications
- Redis caching for OTPs
- Docker containerization
- CI/CD pipeline setup