- Overview
- Authentication Implementations
- Features
- Technologies Used
- Setup Instructions
- Docker
- API Endpoints
- Architecture
- Database Migrations
- Testing
- References
- License
User Service is a Spring Boot application designed to manage user authentication, authorization, and role-based access control. It provides APIs for user signup, login, logout, and role management with support for admin functionalities.
This service supports two authentication implementations:
- JJWT Implementation (
jjwt-authbranch) - Self-managed JWT authentication using the jjwt library with symmetric HMAC keys - OAuth2 Authorization Server (
mainbranch) - Full OAuth2/OpenID Connect 1.0 compliant authorization server using Spring Security
Both implementations can coexist, allowing backward compatibility while supporting modern OAuth2 clients.
The JJWT implementation provides a self-managed JWT authentication system using symmetric HMAC-SHA512 keys.
How it works:
- User authenticates via
/auth/loginwith email and password - Server generates/reuses a symmetric HMAC key stored in the database
- JWT token is created with claims (email, roles, key ID)
- Token and session are stored in the database
- Subsequent requests are validated by verifying the JWT signature and session status
Key characteristics:
- Key Type: Symmetric (HMAC-SHA512)
- Key Storage: Per-user keys stored in
jwttable with expiration - Session Management: Tokens stored in
sessionstable with status tracking - Token Validation: Signature verification + session lookup + claim validation
The OAuth2 implementation provides a full-fledged authorization server compliant with RFC 6749 and OpenID Connect 1.0.
Supported OAuth2 Flows:
- Authorization Code Flow: Standard flow for web applications
- Client Credentials Flow: For service-to-service authentication
- Refresh Token Flow: For token renewal without re-authentication
Key characteristics:
- Key Type: Asymmetric (RSA 2048-bit)
- Key Discovery: JWKS endpoint for public key distribution
- Scope Support: OpenID, profile, email, and custom scopes (read, write)
- Consent Management: User consent tracking for third-party applications
- Token Types: Access tokens, refresh tokens, and OIDC ID tokens
- Metadata Discovery: Standard well-known endpoints
OAuth2.0 Flow Diagram:
- User registration and login
- User authentication and authorization
- Token validation and revocation
- User logout
- Role-based access control
- Admin functionalities for role management
- Per-user symmetric key generation
- Session-based token tracking
- Key rotation with expiration
- OpenID Connect 1.0 support
- Multiple grant type support
- Token introspection and revocation
- Client registration and management
- User consent management
- JWKS endpoint for key distribution
- Java 21
- Spring Boot 4.x
- Spring Security
- Spring Authorization Server (OAuth2)
- Spring Data JPA / Hibernate
- Flyway for database migrations
- MySQL
- JJWT Library 0.12.6 (for legacy authentication)
- Lombok
- Apache Commons Lang3
- Spring Boot Actuator
- Spring Boot DevTools
- Docker / Docker Compose
For local development:
- Java 21
- Maven
- MySQL database
For Docker deployment:
- Docker 20.10+
- Docker Compose 2.0+
Configure the following environment variables for the application:
| Variable | Description | Required |
|---|---|---|
PORT |
Server port | Yes |
DB_URL |
Database JDBC URL (e.g., jdbc:mysql://localhost:3306/userservice) |
Yes |
DB_USERNAME |
Database username | Yes |
DB_PASSWORD |
Database password | Yes |
ADMIN_EMAIL |
Initial admin user email | Yes |
ADMIN_PASSWORD |
Initial admin user password | Yes |
ADMIN_FIRST_NAME |
Admin user first name | Yes |
ADMIN_LAST_NAME |
Admin user last name | Yes |
CLIENT_ID |
OAuth2 client identifier | Yes |
CLIENT_SECRET |
OAuth2 client secret | Yes |
REDIRECT_URI |
OAuth2 redirect URI | Yes |
-
Clone the repository:
git clone https://github.com/spa-raj/userservice.git
-
Navigate to the project directory:
cd userservice -
Choose your branch:
# For OAuth2 Authorization Server implementation (recommended) git checkout main # For JJWT implementation git checkout jjwt-auth
-
Set the required environment variables. Example using export:
export PORT=8081 export DB_URL=jdbc:mysql://localhost:3306/userservice export DB_USERNAME=your_username export DB_PASSWORD=your_password export ADMIN_EMAIL=admin@example.com export ADMIN_PASSWORD=your_admin_password export ADMIN_FIRST_NAME=Admin export ADMIN_LAST_NAME=User export CLIENT_ID=vibevault-client export CLIENT_SECRET=your_client_secret export REDIRECT_URI=https://oauth.pstmn.io/v1/callback # Postman callback for testing
-
Build and run the application:
mvn spring-boot:run
The fastest way to run the userservice with all dependencies:
# Create the shared network (only needed once)
docker network create vibevault-network
# Start the application with MySQL
docker-compose up -dThe application will be available at http://localhost:8081 once the health checks pass.
- Docker 20.10+
- Docker Compose 2.0+
Build the Docker image:
docker build -t userservice:latest .The Dockerfile uses a multi-stage build:
- Build stage: Uses
eclipse-temurin:21-jdk-alpineto compile the application - Runtime stage: Uses
eclipse-temurin:21-jre-alpinefor a smaller final image (~200MB)
The docker-compose.yml includes:
| Service | Description | Port |
|---|---|---|
mysql |
MySQL 8.0 database | 3308:3306 |
userservice |
User Service application | 8081:8081 |
When running with Docker, these environment variables are configured in docker-compose.yml:
| Variable | Default | Description |
|---|---|---|
PORT |
8081 |
Application server port |
DB_URL |
jdbc:mysql://mysql:3306/userservice... |
JDBC connection URL |
DB_USERNAME |
root |
Database username |
DB_PASSWORD |
- | Database password |
ADMIN_EMAIL |
- | Initial admin email |
ADMIN_PASSWORD |
- | Initial admin password |
ADMIN_FIRST_NAME |
- | Admin first name |
ADMIN_LAST_NAME |
- | Admin last name |
CLIENT_ID |
- | OAuth2 client ID |
CLIENT_SECRET |
- | OAuth2 client secret |
REDIRECT_URI |
- | OAuth2 redirect URI |
ISSUER_URI |
http://localhost:8081 |
OAuth2 issuer URI (use http://userservice:8081 for Docker) |
# Start services
docker-compose up -d
# View logs
docker-compose logs -f userservice
# Stop services
docker-compose down
# Rebuild after code changes
docker-compose up -d --build
# Remove volumes (reset database)
docker-compose down -vFor communication with other services (e.g., productservice) in Docker:
-
Create the shared network (if not already created):
docker network create vibevault-network
-
Configure the issuer URI: When running in Docker, set
ISSUER_URI=http://userservice:8081so that JWT tokens have the correct issuer claim for inter-service validation. -
Network topology:
┌─────────────────────────────────────────────────────────┐ │ vibevault-network │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ userservice │◄────►│productservice│ │ │ │ :8081 │ │ :8080 │ │ │ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────┘ -
JWT validation: Other services can validate tokens by fetching the JWKS from
http://userservice:8081/oauth2/jwkswithin the Docker network.
The container includes health checks that verify the application is running:
# Check container health status
docker-compose ps
# View health check logs
docker inspect --format='{{json .State.Health}}' userservice-app | jqThe health check endpoint is available at /actuator/health.
These endpoints are available in the jjwt-auth branch and also in main for backward compatibility.
Register a new user.
Request Body:
{
"email": "user@example.com",
"name": "John Doe",
"password": "securepassword",
"phone": "1234567890",
"role": "seller"
}Response:
{
"userEmail": "user@example.com",
"name": "John Doe",
"phone": "1234567890",
"role": "Role{name='SELLER', description='The seller/merchant of our products.'}"
}Authenticate a user and return a JWT token.
Request Body:
{
"email": "user@example.com",
"password": "securepassword"
}Response:
{
"status": 200,
"token": "eyJraWQiOiIyMmNiZjZlZS1...",
"sessionId": "fc4f9769-6683-412a-9865-a094a7a2151e"
}Validate a user's token.
Request Headers:
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response:
{
"status": 200,
"email": "user@example.com",
"name": "John Doe",
"phone": "1234567890",
"roles": ["SELLER"]
}Logout a user and invalidate the token.
Request Body:
{
"userEmail": "user@example.com",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Response:
{
"status": 204,
"message": "No Content"
}These endpoints are available only in the main branch (OAuth2 implementation).
Initiates the OAuth2 authorization code flow.
Query Parameters:
| Parameter | Description |
|---|---|
client_id |
The registered client identifier |
response_type |
Must be code for authorization code flow |
scope |
Space-separated list of scopes (e.g., openid profile email) |
redirect_uri |
Callback URL for the authorization code |
state |
Optional state parameter for CSRF protection |
Example:
GET /oauth2/authorize?client_id=vibevault-client&response_type=code&scope=openid%20profile&redirect_uri=http://127.0.0.1:8080/callback&state=xyz
Exchange authorization code for tokens or request tokens directly.
Authorization Code Grant:
curl -X POST http://localhost:8081/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "vibevault-client:client-secret" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=http://127.0.0.1:8080/callback"Client Credentials Grant:
curl -X POST http://localhost:8081/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "vibevault-client:client-secret" \
-d "grant_type=client_credentials" \
-d "scope=read write"Response:
{
"access_token": "eyJraWQiOi...",
"refresh_token": "abc123...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid profile",
"id_token": "eyJhbGciOi..."
}Introspect a token to check its validity.
curl -X POST http://localhost:8081/oauth2/introspect \
-u "vibevault-client:client-secret" \
-d "token=ACCESS_TOKEN"Revoke an access or refresh token.
curl -X POST http://localhost:8081/oauth2/revoke \
-u "vibevault-client:client-secret" \
-d "token=ACCESS_TOKEN"Retrieve the JSON Web Key Set for token verification.
Response:
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "key-id",
"alg": "RS256",
"n": "..."
}
]
}OAuth2 authorization server metadata.
OpenID Connect discovery document.
These endpoints require admin authentication.
Create a new role.
Request Body:
{
"roleName": "BUYER",
"description": "This role is for buyers who purchase products."
}Response:
{
"status": 201,
"roleName": "BUYER",
"description": "This role is for buyers who purchase products.",
"message": "Role created successfully"
}Update an existing role.
Request Body:
{
"roleName": "BUYER",
"description": "Updated description for the buyer role."
}Get all roles.
Response:
{
"status": 200,
"roles": [
{
"roleId": "uuid...",
"name": "SELLER",
"description": "The seller/merchant of our products."
},
{
"roleId": "uuid...",
"name": "BUYER",
"description": "This role is for buyers who purchase products."
}
]
}Get a specific role by ID.
The OAuth2 implementation uses a three-tier security filter chain:
┌─────────────────────────────────────────────────────────────┐
│ Incoming Requests │
└─────────────────────────────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Order 1 │ │ Order 2 │ │ Order 3 │
│ OAuth2 Server │ │ API Resource │ │ Default │
│ │ │ Server │ │ Security │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ /oauth2/* │ │ /auth/* │ │ All other │
│ /.well-known/* │ │ /api/* │ │ endpoints │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ OIDC enabled │ │ JWT validation │ │ Form login │
│ CSRF disabled │ │ CSRF disabled │ │ Session-based │
│ Stateless │ │ Stateless │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
| Aspect | JJWT | OAuth2 Authorization Server |
|---|---|---|
| Standard | Custom JWT | RFC 6749, OpenID Connect 1.0 |
| Key Type | Symmetric (HMAC-SHA512) | Asymmetric (RSA 2048-bit) |
| Key Storage | Per-user in database | In-memory (restart regenerates) |
| Grant Types | Direct authentication | Authorization Code, Client Credentials, Refresh Token |
| Scope Support | No | Yes (openid, profile, email, custom) |
| Consent Management | No | Yes |
| Key Discovery | N/A | JWKS endpoint |
| Token Revocation | Manual (logout) | Built-in endpoint |
| Third-party Integration | Limited | Full OAuth2 ecosystem |
JJWT Tables:
users- User accountsroles- Role definitionsuser_roles- User-role mappingssessions- Active session trackingjwt- JWT signing keys
OAuth2 Tables (additional):
client- Registered OAuth2 clientsauthorization- OAuth2 authorization records and tokensauthorization_consent- User consent grants
Flyway is used for managing database schema migrations. Migration scripts are located in src/main/resources/db/migration.
| Migration | Description |
|---|---|
| V1 | Base schema (users, roles, user_roles) |
| V2 | JWT tables (sessions, jwt) |
| V3 | Additional user fields |
| V4 | Reserved for future use |
| V5 | OAuth2 tables (client, authorization, authorization_consent) |
The application includes comprehensive unit and integration tests.
- AuthControllerTest: Validates authentication endpoints
- AuthControllerMVCTest: Mock MVC tests for authentication
- RoleControllerTest: Tests role management endpoints
- RoleControllerMVCTest: Mock MVC tests for role management
- AuthServiceTest: Tests authentication service logic
- KeyLocatorImplTest: Validates JWT key locator implementation
- RoleServiceTest: Tests role service logic
SonarQube integration is planned for code quality and coverage analysis.
- RSA Key Persistence: In the OAuth2 implementation, RSA keys are regenerated on application restart, invalidating existing tokens. See GitHub Issue #25 for tracking.
This project is licensed under the Apache-2.0 License. See the LICENSE file for details.
