A production-grade, event-driven microservices architecture implementing OTP-based authentication with JWT tokens for secure user identity management and session handling.
- OTP-Based Login: Secure 6-digit one-time passwords with 5-minute expiry
- JWT Token Generation: Cryptographically signed tokens (HMAC SHA256) with 1-hour expiry
- Role-Based Access Control: Support for multiple user roles (USER, ADMIN, etc.)
- Stateless Authentication: JWT-based session management without server-side state
- Distributed Security: Shared JWT utilities across all microservices
- Asynchronous Messaging: Kafka-based pub/sub for loose coupling between services
- Real-time Email Notifications: Immediate OTP delivery via SMTP
- Event Sourcing: Audit trail of OTP requests and generations
- Service Decoupling: Services operate independently without direct dependencies
- Domain-Driven Services: Each service handles one responsibility
- Independent Scaling: Services can scale based on their own load
- Database per Service: Each microservice has dedicated PostgreSQL database
- Service Discovery: Docker DNS-based service-to-service communication
- GraphQL APIs: Type-safe, flexible query language for each service
- GraphiQL Playground: Built-in API exploration tools
- Inter-Service Communication: REST calls for OTP verification
- Future OpenSearch Integration: Full-text search and analytics capability
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Compose Network β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β β Admin Auth Svc β β OTP Generator β β
β β (8081) βββββΆβ Service(8082) β β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β β β β
β β Kafka Topics β β
β ββotp-requestedβββββββββββΆβ β
β β otp-generated β
β β ββββββββββ¬ββββββββ β
β β β β
β βΌ βΌ β
β ββββββββββββββββββββββββββββββββββββ β
β β Notification Service (8083) β β
β β Kafka Consumer β β
β β (SMTP Email Sender) β β
β ββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββ β
β β Kafka (9092) β β
β β Message Broker β β
β ββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
β β PostgreSQL (localhost:5432) β β
β β - auth_service_db β β
β β - otp_generator_db β β
β β - notification_service_db β β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
1. AUTHENTICATION REQUEST
CLIENT --GraphQL--> Admin Auth Service (8081)
ββ Publishes: "otp-requested" ---> Kafka
2. OTP GENERATION
OTP Generator Svc (8082) <-- Consumes: "otp-requested"
ββ Generates 6-digit OTP
ββ Saves to DB (5-min expiry)
ββ Publishes: "otp-generated" ---> Kafka
3. EMAIL NOTIFICATION
Notification Service (8083) <-- Consumes: "otp-generated"
ββ Extracts email & OTP
ββ Sends SMTP email --> Gmail Server --> User Email
4. OTP VERIFICATION & LOGIN
CLIENT --GraphQL--> Admin Auth Service (8081)
ββ Calls: OTP Service GraphQL
ββ verifyOtp(email, otp)
ββ Generates JWT Token
ββ Returns Token to Client
5. AUTHENTICATED REQUESTS
CLIENT --Request + JWT Token--> Protected Services
ββ JwtAuthFilter validates token
ββ Sets user context
ββ Processes authenticated request
Java/Java/
βββ admin-auth-service/
β βββ src/
β β βββ main/java/com/example/
β β β βββ AuthGraphQLResolver.java # GraphQL mutations
β β β βββ AuthService.java # Business logic
β β β βββ User.java # Entity model
β β βββ resources/
β β βββ application.properties # Configuration
β β βββ graphql/schema.graphqls # GraphQL schema
β βββ Dockerfile # Container image
β βββ pom.xml # Maven dependencies
β βββ mvnw # Maven wrapper
β
βββ common-security-lib/
β βββ src/main/java/com/example/
β β βββ JwtUtil.java # JWT utilities
β β βββ JwtAuthFilter.java # JWT servlet filter
β β βββ JwtContext.java # Thread-local context
β βββ pom.xml # Maven POM
β βββ mvnw
β
βββ otp-generator-service/
β βββ src/
β β βββ main/java/com/example/
β β β βββ OtpMutationResolver.java # GraphQL mutations
β β β βββ OtpService.java # OTP logic
β β β βββ OtpRequestListener.java # Kafka consumer
β β β βββ UserOtp.java # Entity model
β β βββ resources/application.properties
β βββ Dockerfile
β βββ pom.xml
β βββ mvnw
β
βββ notification-service/
β βββ src/
β β βββ main/java/com/example/
β β β βββ OtpGeneratedListener.java # Kafka consumer
β β β βββ EmailService.java # SMTP sender
β β βββ resources/application.properties
β βββ Dockerfile
β βββ pom.xml
β βββ mvnw
β
βββ OpenSearchJavaClientLocalDemo/
β βββ src/main/java/com/example/
β β βββ OpenSearchClient.java # Search demo
β βββ pom.xml
β
βββ docker-compose.yml # Orchestration config
- Spring Boot: 3.5.4
- Java: JDK 17
- Build Tool: Maven 3.9.0
- GraphQL: Spring GraphQL 3.5.4
- Apache Kafka: Message broker for async events
- Servlet API: Jakarta 5.0.0
- JWT (JJWT): 0.11.5 (HMAC SHA256 signing)
- Spring Security: Integrated via common-security-lib
- SMTP: Gmail-based email delivery
- PostgreSQL: Primary database (3 instances)
- Spring Data JPA: ORM and database abstraction
- Jackson: JSON serialization/deserialization
- OpenSearch: 2.11.0 (full-text search demo)
- Docker: Service containerization
- Docker Compose: Multi-container orchestration
- Docker Networking: Service-to-service communication
Purpose: Main authentication entry point for OTP-based user login
Responsibilities:
- Accept OTP requests from clients
- Publish OTP generation events to Kafka
- Call OTP Service to verify OTP codes
- Generate JWT tokens upon successful authentication
- Maintain user registry with roles
GraphQL Schema:
type Query {
getUser(email: String!): User
}
type Mutation {
requestOtp(email: String!): Boolean
loginWithOtp(email: String!, otp: String!): JwtResponse
}
type JwtResponse {
token: String!
}
type User {
id: Long!
email: String!
role: String!
active: Boolean!
}Technology:
- Spring Boot 3.5.4
- Spring Security with JWT
- GraphQL API
- Kafka Producer
- PostgreSQL
Configuration (application.properties):
server.port=8081
spring.datasource.url=jdbc:postgresql://host.docker.internal:5432/auth_service_db
spring.graphql.graphiql.enabled=true
jwt.expiration=3600000
spring.kafka.bootstrap-servers=kafka:9092Purpose: OTP generation and validation service
Responsibilities:
- Generate cryptographically secure 6-digit OTPs
- Persist OTPs with 5-minute expiry time
- Validate OTP codes from login requests
- Consume Kafka OTP request events
- Publish OTP generation events
GraphQL Schema:
type Mutation {
verifyOtp(email: String!, otp: String!): Boolean
}Kafka Topics:
-
Consumer:
otp-requested(from Admin Auth Service){ "email": "user@example.com", "timestamp": "2026-04-19T10:30:00Z" } -
Producer:
otp-generated(to Notification Service){ "email": "user@example.com", "otp": "123456" }
Technology:
- Spring Boot 3.5.4
- Spring Data JPA
- GraphQL API
- Kafka Consumer & Producer
- PostgreSQL
Configuration (application.properties):
server.port=8082
spring.datasource.url=jdbc:postgresql://host.docker.internal:5432/otp_generator_db
spring.graphql.graphiql.enabled=true
spring.kafka.bootstrap-servers=kafka:9092
spring.kafka.consumer.group-id=otp-generator-group
otp.expiry.minutes=5Purpose: Email notifications for OTP delivery
Responsibilities:
- Consume OTP generation events from Kafka
- Extract OTP and recipient email
- Send OTP via SMTP email
- Provide email delivery confirmation
Kafka Consumer:
- Topic:
otp-generated - Group:
notification-group - Message Format:
{ "email": "user@example.com", "otp": "123456" }
Email Template:
Subject: Your OTP for Login
Hello,
Your One-Time Password (OTP) is: 123456
This OTP is valid for 5 minutes only.
Do not share this OTP with anyone.
Regards,
AuthFlow Team
Technology:
- Spring Boot 3.5.4
- Spring Mail (JavaMailSender)
- Kafka Consumer
- SMTP (Gmail relay)
Configuration (application.properties):
server.port=8083
spring.kafka.bootstrap-servers=kafka:9092
spring.kafka.consumer.group-id=notification-group
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=zenouchiha01@gmail.com
spring.mail.password=<app-password>
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=truePurpose: Shared JWT security utilities for all microservices
Key Components:
- Generates JWT tokens with email and role claims
- Parses and validates JWT tokens
- Handles signature verification (HMAC SHA256)
- Extracts claims from tokens
- Servlet filter for incoming request validation
- Extracts Bearer token from Authorization header
- Validates token signature and expiration
- Sets authenticated user context
- Thread-local storage for current user information
- Provides thread-safe access to email and role
- Maintains request-scoped authentication state
Token Structure:
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "user@example.com",
"role": "USER",
"iat": 1713590400,
"exp": 1713594000
}Maven Dependency (add to other services):
<dependency>
<groupId>com.example</groupId>
<artifactId>common-security-lib</artifactId>
<version>1.0.0</version>
</dependency>Purpose: Demonstration of OpenSearch integration for full-text search
Capabilities:
- Index JSON documents to OpenSearch indices
- Execute full-text search queries
- Retrieve documents by ID
- Real-time document indexing
Use Cases (Future):
- User activity logs and auditability
- Search across authentication records
- Analytics and reporting
Technology:
- OpenSearch REST High-Level Client 2.11.0
- Log4j for logging
- Docker Desktop: Latest version (includes Docker and Docker Compose)
- PostgreSQL: Running on localhost:5432
- Maven: 3.9.0 or higher (or use mvnw wrapper)
- Java: JDK 17 or higher
cd Java/Java# Connect to PostgreSQL
psql -U postgres
# Create databases
CREATE DATABASE auth_service_db;
CREATE DATABASE otp_generator_db;
CREATE DATABASE notification_service_db;
# Exit
\q# Build each service individually or use the wrapper script
cd admin-auth-service && mvn clean package -DskipTests
cd ../otp-generator-service && mvn clean package -DskipTests
cd ../notification-service && mvn clean package -DskipTests
cd ../common-security-lib && mvn clean package -DskipTests
cd ..docker-compose up -d
# Verify services are running
docker-compose ps
# View logs
docker-compose logs -f# Check Admin Auth Service
curl http://localhost:8081/graphiql
# Check OTP Generator Service
curl http://localhost:8082/graphiql
# Check Notification Service
curl http://localhost:8083/healthGraphQL Query (via http://localhost:8081/graphiql):
mutation {
requestOtp(email: "john.doe@example.com")
}Response:
{
"data": {
"requestOtp": true
}
}GraphQL Mutation (via http://localhost:8081/graphiql):
mutation {
loginWithOtp(email: "john.doe@example.com", otp: "123456") {
token
}
}Response:
{
"data": {
"loginWithOtp": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInJvbGUiOiJVU0VSIiwiaWF0IjoxNzEzNTkwNDAwLCJleHAiOjE3MTM1OTQwMDB9.signature"
}
}
}HTTP Request with Bearer Token:
curl -X GET http://localhost:8081/api/profile \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."GraphQL Mutation (via http://localhost:8082/graphiql):
mutation {
verifyOtp(email: "john.doe@example.com", otp: "123456")
}- OTP Generation: 6-digit random code with 5-minute validity
- Email Delivery: SMTP-based secure OTP transmission
- Verification: Server-side OTP validation
- Token Generation: JWT issued upon successful OTP verification
- Request Authentication: JWT validated on every protected request
- Algorithm: HMAC SHA256
- Expiration: 1 hour (3600000 ms)
- Claims: Email (sub), Role (role), Issue Time (iat), Expiration (exp)
- Signature: Server-side secret key validation
- Internal Network: All services on Docker private network
- Service Discovery: DNS-based via service names
- No External Exposure: Only specified ports exposed
# Scale OTP Generator Service (more OTP requests)
docker-compose up -d --scale otp-generator-service=3
# Scale Notification Service (more email sends)
docker-compose up -d --scale notification-service=2- Kafka consumer groups handle automatic load distribution
- Multiple service instances can consume from same Kafka topic
- Kafka ensures each message is processed once across the group
- Kafka Batch Size: Adjust
batch.sizefor throughput - Pool Size: Configure
spring.jdbc.hikari.maximum-pool-size - OTP Expiry: Adjust
otp.expiry.minutesas needed - JWT Expiration: Modify
jwt.expirationbased on security requirements
# JWT Configuration
JWT_SECRET=your-secret-key
JWT_EXPIRATION=3600000
# Database Configuration
DB_URL=jdbc:postgresql://host.docker.internal:5432/auth_service_db
DB_USERNAME=postgres
DB_PASSWORD=password
# Kafka Configuration
KAFKA_BROKERS=kafka:9092
# Email Configuration
MAIL_USERNAME=zenouchiha01@gmail.com
MAIL_PASSWORD=app-specific-passwordEdit src/main/resources/application.properties in each service:
- Port configuration
- Database URL and credentials
- Kafka broker addresses
- Email settings
- JWT secrets
# Check logs
docker-compose logs admin-auth-service
docker-compose logs otp-generator-service
docker-compose logs notification-service
# Common issues:
# - PostgreSQL not running on localhost:5432
# - Kafka not starting (port 9092 in use)
# - Insufficient memory for JVM# Verify Kafka is running
docker-compose ps kafka
# Check Kafka logs
docker-compose logs kafka
# Restart Kafka
docker-compose restart kafka- Verify Notification Service logs:
docker-compose logs notification-service - Check email configuration in
notification-service/application.properties - Ensure Gmail app password is set correctly
- Check Gmail "Allow less secure apps" if using Gmail account
# Verify token expiration (should be 1 hour from issue time)
# Check token payload at https://jwt.io
# Ensure Authorization header format: "Bearer <token>"- Admin Auth Service: http://localhost:8081/graphiql
- OTP Generator Service: http://localhost:8082/graphiql
| Topic | Producer | Consumer | Schema |
|---|---|---|---|
otp-requested |
Admin Auth Svc | OTP Gen Svc | {email, timestamp} |
otp-generated |
OTP Gen Svc | Notification Svc | {email, otp} |
| Service | Port | Protocol | Purpose |
|---|---|---|---|
| Admin Auth | 8081 | HTTP/GraphQL | Authentication API |
| OTP Generator | 8082 | HTTP/GraphQL | OTP verification API |
| Notification | 8083 | HTTP | Email service |
| Kafka | 9092 | TCP | Message broker |
| Zookeeper | 2181 | TCP | Kafka coordination |
docker-compose up -d- Create Helm charts for each service
- Deploy PostgreSQL as StatefulSet
- Deploy Kafka as StatefulSet
- Use ConfigMaps for configuration
- Use Secrets for sensitive data (API keys, passwords)
- Build JAR files:
mvn clean package - Copy JARs to servers
- Install Java JDK 17
- Configure environment variables
- Run:
java -jar service.jar
- Version Control: Git (GitHub/GitLab)
- CI Tool: Jenkins/GitHub Actions
- Build: Maven clean package
- Test: Unit tests + integration tests
- Artifact: Docker image push to registry
- Deploy: Docker Compose or Kubernetes
- Monitoring: ELK Stack or Prometheus/Grafana
name: Build and Deploy AuthFlow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Build Docker images
run: docker-compose build
- name: Push to Registry
run: docker push registry/authflow:latest- Create new Maven module with Spring Boot starter
- Implement GraphQL resolvers for API
- Add Kafka producer/consumer as needed
- Create PostgreSQL schema
- Add to docker-compose.yml
- Include common-security-lib dependency
// Custom OTP validation logic
public boolean validateOtp(String email, String otp, long maxAgeMinutes) {
UserOtp userOtp = repository.findByEmail(email);
return userOtp != null &&
userOtp.getOtp().equals(otp) &&
Duration.between(userOtp.getExpiryTime(), LocalDateTime.now()).toMinutes() <= maxAgeMinutes;
}- Spring Boot Documentation
- GraphQL Java Documentation
- Apache Kafka Documentation
- JWT Introduction
- Docker Compose Documentation
- PostgreSQL Documentation
[Add your license here]
Contributions are welcome! Please follow these guidelines:
- Create a feature branch
- Make your changes with clear commit messages
- Add tests for new functionality
- Submit a pull request with documentation
For issues, questions, or suggestions:
- Open an issue on the repository
- Contact the development team
- Check the Troubleshooting section
Version: 1.0.0
Status: β
Production-Ready
Last Updated: April 2026
Maintainers: Development Team