A secure and scalable backend service implementing OTP-based authentication and user management, built with Go, PostgreSQL, and Redis.
- π OTP-based authentication (SMS-less, console logging)
- π¦ Rate limiting (3 OTP requests per phone per 10 minutes)
- π JWT token-based session management
- π₯ User management with pagination and search
- π RESTful API with Swagger documentation
- π³ Fully containerized with Docker
- ποΈ Clean architecture implementation
- π Phone number validation (E.164 format)
- β‘ Redis for OTP storage and rate limiting
- ποΈ PostgreSQL for user data persistence
- Language: Go 1.25.0
- Framework: Fiber v2.52.9
- Database: PostgreSQL 15
- Cache/Storage: Redis 7
- Authentication: JWT
- Documentation: Swagger/OpenAPI
- Containerization: Docker & Docker Compose
βββ cmd/ # Application entry point
βββ internal/
β βββ config/ # Configuration management
β βββ handler/ # HTTP handlers (controllers)
β βββ service/ # Business logic
β βββ repository/ # Data access layer
β βββ model/ # Data models and DTOs
β βββ middleware/ # HTTP middleware
βββ pkg/ # Reusable packages
β βββ jwt/ # JWT utilities
β βββ utils/ # General utilities
βββ docs/ # API documentation
PostgreSQL + Redis Combination:
- PostgreSQL: Perfect for persistent user data with ACID compliance, excellent for relational data and provides strong consistency for user records
- Redis: Ideal for temporary OTP storage with TTL support, fast in-memory operations for rate limiting, and automatic expiration handling
- Benefits: Best of both worlds - reliability for important data, speed for temporary data
- Docker and Docker Compose installed
- Go 1.21+ (for local development)
- Make (optional, for convenience commands)
git clone <repository-url>
cd golang-test-dekamond# Start all services (PostgreSQL, Redis, and the application)
docker-compose up -d
# View logs
docker-compose logs -f appThe API will be available at http://localhost:8080
# Start databases only
docker-compose up -d postgres redis
# Install dependencies
go mod download
# Run the application
go run cmd/main.goOnce the service is running, access the Swagger documentation at:
- Swagger UI: http://localhost:8080/swagger/index.html
POST /api/v1/auth/send-otp- Send OTP to phone numberPOST /api/v1/auth/verify-otp- Verify OTP and get JWT token
GET /api/v1/users/profile- Get current user profileGET /api/v1/users- Get paginated list of users with searchGET /api/v1/users/{id}- Get specific user by ID
GET /health- Service health status
curl -X POST http://localhost:8080/api/v1/auth/send-otp \
-H "Content-Type: application/json" \
-d '{"phone_number": "+1234567890"}'Response:
{
"message": "OTP sent successfully"
}Console Output:
OTP for +1234567890: 123456
curl -X POST http://localhost:8080/api/v1/auth/verify-otp \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+1234567890",
"otp_code": "123456"
}'Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"phone_number": "+1234567890",
"registered_at": "2024-01-15T10:30:00Z"
}
}curl -X GET "http://localhost:8080/api/v1/users?page=1&page_size=10" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"Response:
{
"users": [
{
"id": 1,
"phone_number": "+1234567890",
"registered_at": "2024-01-15T10:30:00Z"
}
],
"total": 1,
"page": 1,
"page_size": 10,
"total_pages": 1
}Environment variables can be set in .env file (copy from .env.example):
# Server
SERVER_HOST=localhost
SERVER_PORT=8080
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=otp_service
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
# JWT
JWT_SECRET=your-secret-key
JWT_EXPIRY_HOURS=24
# OTP
OTP_LENGTH=6
OTP_EXPIRY_MINUTES=2
OTP_MAX_ATTEMPTS=3
OTP_RATE_LIMIT_MINUTES=10Using Make (recommended):
# Setup development environment
make dev-setup
# Install dependencies
make deps
# Run locally
make run
# Build application
make build
# Generate Swagger docs
make swagger
# Start databases only
make db-up
# Docker operations
make docker-build
make docker-up
make docker-down
make docker-logsUsing Go directly:
# Run
go run cmd/main.go
# Build
go build -o bin/otp-service cmd/main.go
# Test
go test ./...- Rate Limiting: Max 3 OTP requests per phone number per 10 minutes
- OTP Expiry: OTP expires after 2 minutes
- JWT Security: Secure token-based authentication
- Input Validation: Phone number format validation (E.164)
- Attempt Limiting: Max 3 verification attempts per OTP
The API returns consistent error responses:
{
"error": "error_code",
"message": "Human readable error message"
}Common error codes:
rate_limit_exceeded- Too many OTP requestsinvalid_otp- Wrong OTP codeotp_expired- OTP has expiredunauthorized- Invalid/missing JWT tokeninvalid_phone_number- Invalid phone format
# Run all tests
make test
# Or with go
go test -v ./...- π‘οΈ Multi-Layer Rate Limiting:
- OTP requests: 3 per phone per 10 minutes
- Global API: 100 requests per IP per minute
- Verification attempts: 3 per OTP
- π Timing Attack Prevention: Constant-time OTP comparison
- π« Input Validation: Enhanced phone number validation with DoS protection
- π‘οΈ Security Headers: Helmet middleware for XSS/CSRF protection
- π JWT Security: HS256 signing with proper token validation
- π Input Sanitization: All inputs sanitized and length-validated
- β±οΈ Context Timeouts: All database operations have timeout protection
- π Graceful Shutdown: Production-ready server lifecycle management
- Environment Variables: Set strong JWT secret and database passwords
- HTTPS: Use TLS/SSL in production
- Database: Use connection pooling and proper indexing
- Monitoring: Add logging and monitoring solutions
- Rate Limiting: Additional rate limiting at API gateway level recommended
- SMS Integration: Replace console logging with actual SMS service
- Security: All security features are production-ready
# Build and run everything
docker-compose up --build
# Run in background
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down
# Clean up volumes
docker-compose down -vcurl http://localhost:8080/healthResponse:
{"status": "healthy"}- Fork the repository
- Create a feature branch
- Make changes with tests
- Submit a pull request
MIT License