The Authentication system provides secure service-to-service communication using RS256 JWT tokens.
The system employs a Dual-Token Strategy:
- User Tokens: Issued to clients after login. Used to authorize mutations (
POST,PUT,DELETE). - Service Tokens: Signed by the API to authenticate itself against external services (e.g.,
DemoAPI).
- Purpose: Specialized verticle for JWT management and token issuance.
- Responsibilities:
- Key Normalization: Reconstructs RSA PEM keys from environment variables.
- Event Bus Listener:
auth.token.get: Returns cached Service Token.auth.token.issue: Generates new User Token (requires username).
- Encapsulation: Owns the
Rs256TokenService.
- Purpose: Manages the life-cycle of JWT tokens.
- Key Features:
- Service Token Generation: Signs tokens for service-to-service calls (cached, auto-refresh).
- User Token Generation: Generates short-lived (15m) tokens for users.
- Event Loop Protection: Uses
vertx.executeBlockingfor RSA signing to prevent blocking the event loop during high load (v4.3 Update). - Signing: Uses the loaded RSA Private Key.
- Purpose: Orchestrator and Key Provider.
- Boot Flow:
- Retrieves
RSA_PRIVATE_KEYandRSA_PUBLIC_KEYfrom environment. - Deploys
AuthVerticle(Private Key) for signing. - Deploys
HttpVerticle(Public Key) for local verification.
- Retrieves
To support containerized environments (Docker Swarm/Kubernetes), keys are loaded with a File-First Precedence:
- Checks for
RSA_PRIVATE_KEY_FILE. If present, reads content from the mounted secret. - Falls back to
RSA_PRIVATE_KEYenvironment variable (Legacy/Dev). This ensures keys are never exposed as plain-text environment variables in production.
The user authenticates to receive a token. The request is guarded by a dedicated Circuit Breaker (auth-login) to fail fast during overloads.
Observability: Login attempts are tracked via
api_auth_attempts_totaland breaker status viacircuit_breaker_state{name="auth-login"}.
sequenceDiagram
participant Client
participant AuthCtrl as AuthController
participant Val as SchemaValidator
participant UserV as UserVerticle
participant AuthV as AuthVerticle
Client->>AuthCtrl: POST /login (LoginRequestDTO)
AuthCtrl->>Val: validateLogin(body)
Note over Val: Fail-Fast if invalid
AuthCtrl->>AuthCtrl: Circuit Breaker Check
AuthCtrl->>UserV: users.authenticate (LoginRequestDTO)
Note over UserV: BCrypt Check (Blocking)
UserV-->>AuthCtrl: User OK
AuthCtrl->>AuthV: auth.token.issue
Note over AuthV: Sign (executeBlocking)
AuthV-->>AuthCtrl: JWT Token
AuthCtrl-->>Client: 200 OK { token }
The Reactive API authenticates itself to the Demo service for IP verification.
graph TD
A[Reactive API] -->|1. Sign with Private Key| B[JWT Service Token]
B -->|2. Bearer Header| C[Demo Service]
C -->|3. Verify with Public Key| D{Authorized?}
D -->|Yes| E[Allow Request]
D -->|No| F[403 Forbidden]
- Algorithm: RS256 (RSA Signature with SHA-256).
- Public Key: Located in the Demo service (
JwtAuthenticationFilter.java). - Private Key: Located in the Reactive API (
MainVerticle.java).
The VerificationHandler in the auth package uses the Event Bus to fetch tokens.
- Request: Sends a message to
auth.token.get. - Response: Receives a String token or a failure if the
AuthVerticleis not configured. - V1 Routes: Skip authentication.
- V3 Routes: Require token injection via the
Authorization: Bearer <token>header.