Java API Gateway Authentication
This document describes the JWT authentication that has been added to the Spring Boot API gateway.
Overview
Authentication is handled at the gateway layer using JSON Web Tokens (JWT).
The flow is intentionally simple:
- A client sends a username and password to
POST /api/auth/login. - The gateway validates those credentials against configured values.
- If the credentials are valid, the gateway returns a signed bearer token.
- The client sends that token in the
Authorizationheader when calling protected endpoints.
This implementation uses stateless authentication. No server-side session is created.
What Is Protected
Public endpoints
POST /api/auth/loginGET /api/traffic/healthGET /swagger-ui/**GET /v3/api-docs/**- Static resources such as
/,/index.html,/app.js,/styles.css, and/images/**
Protected endpoints
GET /api/traffic/actionPOST /api/traffic/action
Requests to protected endpoints must include a bearer token.
Configuration
Authentication and JWT settings are configured in application.yml and can be overridden with environment variables.
| Property | Environment variable | Default | Notes |
|---|---|---|---|
security.jwt.issuer |
JWT_ISSUER |
traffic-api-gateway |
Used as the JWT issuer claim |
security.jwt.secret |
JWT_SECRET |
change-this-secret-key-to-a-very-long-random-value |
Must be at least 32 bytes for HS256 |
security.jwt.expiration-minutes |
JWT_EXPIRATION_MINUTES |
60 |
Token lifetime |
security.auth.username |
JWT_AUTH_USERNAME |
admin |
Login username |
security.auth.password |
JWT_AUTH_PASSWORD |
admin123 |
Login password |
Security Notes
- The default username, password, and secret are for local development only.
- Replace
JWT_SECRETbefore deploying anywhere outside a local environment. - This implementation currently uses a single configured username and password.
- There is no refresh-token flow yet.
- Invalid or expired tokens are treated as unauthenticated requests.
How It Works Internally
AuthController
POST /api/auth/login accepts a JSON payload:
{
"username": "admin",
"password": "admin123"
}
If the credentials match the configured values, the controller returns:
{
"tokenType": "Bearer",
"accessToken": "<jwt>",
"expiresIn": 3600,
"timestamp": 1710000000000
}
JwtService
The JWT service:
- Validates the configured signing secret at startup
- Signs tokens using HS256
- Stores the username in the
subclaim - Adds
iss,iat, andexpclaims - Parses and validates incoming tokens
JwtAuthenticationFilter
The request filter:
- Reads the
Authorizationheader - Expects the format
Bearer <token> - Validates the token
- Extracts the username from the token subject
- Adds an authenticated principal to the Spring Security context
SecurityConfig
Spring Security is configured to:
- Disable CSRF for this stateless API
- Disable HTTP Basic authentication
- Use stateless session management
- Allow public access to login, health, Swagger, and static assets
- Require authentication for traffic action endpoints
Running Locally
Option 1: Docker Compose
The docker-compose.yml file includes these JWT-related environment variables for the gateway:
JWT_SECRET: change-this-secret-key-to-a-very-long-random-value
JWT_AUTH_USERNAME: admin
JWT_AUTH_PASSWORD: admin123
JWT_EXPIRATION_MINUTES: "60"
Start the services:
docker-compose up --build
Option 2: Maven
From the java-api-gateway directory:
mvn spring-boot:run
If you want to override the defaults, set environment variables first.
Example PowerShell:
$env:JWT_SECRET="replace-with-a-long-random-secret-of-at-least-32-characters"
$env:JWT_AUTH_USERNAME="admin"
$env:JWT_AUTH_PASSWORD="admin123"
$env:JWT_EXPIRATION_MINUTES="60"
mvn spring-boot:run
Usage Examples
PowerShell
Authenticate:
$loginBody = @{
username = "admin"
password = "admin123"
} | ConvertTo-Json
$tokenResponse = Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/auth/login" -ContentType "application/json" -Body $loginBody
$token = $tokenResponse.accessToken
Call a protected endpoint:
$headers = @{ Authorization = "Bearer $token" }
Invoke-RestMethod -Method Get -Uri "http://localhost:8080/api/traffic/action" -Headers $headers
Call the prediction endpoint with custom observations:
$headers = @{ Authorization = "Bearer $token" }
$body = @{
observations = @(0.12, 0.33, 0.41, 0.55, 0.62, 0.70, 0.81, 0.90, 0.95)
metadata = "morning-peak"
} | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/traffic/action" -Headers $headers -ContentType "application/json" -Body $body
curl
Authenticate and capture the token:
TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' | jq -r '.accessToken')
Call a protected endpoint:
curl -X GET http://localhost:8080/api/traffic/action \
-H "Authorization: Bearer $TOKEN"
Call the prediction endpoint with custom observations:
curl -X POST http://localhost:8080/api/traffic/action \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"observations": [0.12, 0.33, 0.41, 0.55, 0.62, 0.70, 0.81, 0.90, 0.95],
"metadata": "morning-peak"
}'
Swagger Usage
Swagger UI is still publicly reachable.
Open:
http://localhost:8080/swagger-ui/index.html
You can authenticate by first calling POST /api/auth/login, copying the returned accessToken, and then using the Swagger Authorize button with:
Bearer <your-token>