Authentication
Authentication in the MATIH Platform is the process of verifying the identity of users, services, and API clients before granting access to any resource. The platform implements a layered authentication model built on JWT tokens, with support for OAuth2/SCIM integration, multi-factor authentication (MFA), API keys, and service-to-service tokens.
Authentication Architecture
The authentication system is implemented across two primary components:
| Component | Location | Responsibility |
|---|---|---|
| Commons JWT Library | commons/commons-java/.../security/authentication/ | Token generation, validation, and claim extraction |
| IAM Service | control-plane/iam-service/ | User registration, login flows, MFA, session management, API keys |
All authentication flows result in a signed JWT token that carries the user identity, tenant context, and role assignments. This token is then used for authorization decisions throughout the platform.
JWT Token Model
MATIH uses the JJWT library (version 0.12.3) for token generation and validation. The platform defines four distinct token types, each with its own purpose and lifetime.
Token Types
| Token Type | Purpose | Default Lifetime | Claim: type |
|---|---|---|---|
| Access Token | Authenticate API requests | 15 minutes | access |
| Refresh Token | Obtain new access tokens | 7 days | refresh |
| Service Token | Inter-service communication | 5 minutes | service |
| API Key Token | Long-lived programmatic access | Configurable | api_key |
Access Token Claims
Every access token contains the following claims:
{
"jti": "550e8400-e29b-41d4-a716-446655440000",
"sub": "user-123",
"iss": "matih-platform",
"iat": 1707782400,
"exp": 1707783300,
"type": "access",
"tenant_id": "acme-corp",
"roles": ["analyst", "operator"]
}| Claim | Description |
|---|---|
jti | Unique token identifier (UUID) for revocation tracking |
sub | User identifier (subject) |
iss | Token issuer, verified during validation |
iat | Issued-at timestamp |
exp | Expiration timestamp |
type | Token type discriminator |
tenant_id | The tenant to which this user belongs |
roles | Set of RBAC roles assigned to the user |
Token Generation
The JwtTokenProvider class handles all token generation. It uses HMAC-SHA signing with a configurable secret key:
// Token provider initialization
JwtTokenProvider provider = new JwtTokenProvider(
secretKey, // HMAC signing key (from K8s secret)
"matih-platform", // Issuer identifier
Duration.ofMinutes(15), // Access token lifetime
Duration.ofDays(7) // Refresh token lifetime
);
// Generate a token pair (access + refresh)
TokenPair tokens = provider.generateTokenPair(userId, tenantId, roles);
// Generate an access token with additional claims
String token = provider.generateAccessToken(
userId,
tenantId,
roles,
Map.of("department", "engineering", "mfa_verified", true)
);Token Validation
The JwtTokenValidator class validates tokens by verifying the signature, issuer, and expiration:
JwtTokenValidator validator = new JwtTokenValidator(secretKey, "matih-platform");
// Validate any token
ValidationResult result = validator.validate(token);
if (result.isValid()) {
Claims claims = result.getClaims();
String userId = claims.getSubject();
String tenantId = claims.get("tenant_id", String.class);
}
// Validate specifically as an access token
ValidationResult accessResult = validator.validateAccessToken(token);
// Extract specific claims
Optional<String> tenantId = validator.extractTenantId(token);
Set<String> roles = validator.extractRoles(token);The validator produces structured error messages for each failure mode:
| Failure | Error Message |
|---|---|
| Expired token | "Token has expired" |
| Invalid signature | "Invalid token signature" |
| Malformed JWT | "Malformed token" |
| Wrong token type | "Token is not an access token" |
Login Flow
The primary authentication flow is handled by the AuthController at /api/v1/auth. The login process supports both standard credential-based authentication and MFA-protected logins.
Standard Login (Without MFA)
Client AuthController AuthenticationService
| | |
|-- POST /api/v1/auth/login ->| |
| { email, password } |-- login(request, ...) ------>|
| | |-- Validate credentials
| | |-- Check account status
| | |-- Generate token pair
| |<-- AuthResponse -------------|
|<-- 200 { accessToken, | |
| refreshToken, | |
| expiresIn } | |Login Request
curl -X POST https://api.matih.ai/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "analyst@acme.com",
"password": "secure-password"
}'Login Response (Success, No MFA)
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiJ9...",
"tokenType": "Bearer",
"expiresIn": 900
}Login Response (MFA Required)
When MFA is enabled, the login endpoint returns an MFA challenge instead of tokens:
{
"mfaRequired": true,
"challengeId": "ch_abc123def456",
"availableMethods": ["TOTP", "SMS", "BACKUP_CODE"],
"expiresIn": 300
}The client must then complete the MFA challenge to obtain tokens.
Multi-Factor Authentication (MFA)
MATIH supports three MFA methods, managed by the MfaController at /api/v1/mfa:
| Method | Service | Description |
|---|---|---|
| TOTP | MfaService | Time-based one-time passwords via authenticator apps (Google Authenticator, Authy) |
| SMS | SmsMfaService | Verification codes sent via SMS to a registered phone number |
EmailMfaService | Verification codes sent to the user's email address |
All methods generate backup codes upon enrollment for account recovery.
TOTP Enrollment Flow
TOTP enrollment is a two-step process: initiation and verification.
Step 1: Initiate Enrollment
curl -X POST https://api.matih.ai/api/v1/mfa/totp/enroll \
-H "Authorization: Bearer {accessToken}"Response:
{
"secret": "JBSWY3DPEHPK3PXP",
"qrCodeUri": "otpauth://totp/MATIH:analyst@acme.com?secret=JBSWY3DPEHPK3PXP&issuer=MATIH",
"status": "PENDING_VERIFICATION"
}The client displays the QR code for the user to scan with their authenticator app.
Step 2: Verify Enrollment
curl -X POST https://api.matih.ai/api/v1/mfa/totp/verify \
-H "Authorization: Bearer {accessToken}" \
-H "Content-Type: application/json" \
-d '{ "code": "123456" }'Response (includes backup codes):
{
"status": "ACTIVE",
"backupCodes": [
"a1b2c3d4",
"e5f6g7h8",
"i9j0k1l2",
"m3n4o5p6",
"q7r8s9t0"
]
}SMS Enrollment Flow
# Step 1: Initiate SMS enrollment with phone number
curl -X POST https://api.matih.ai/api/v1/mfa/sms/enroll \
-H "Authorization: Bearer {accessToken}" \
-H "Content-Type: application/json" \
-d '{ "phoneNumber": "+1-555-0123" }'
# Step 2: Verify with received code
curl -X POST https://api.matih.ai/api/v1/mfa/sms/verify \
-H "Authorization: Bearer {accessToken}" \
-H "Content-Type: application/json" \
-d '{ "code": "789012" }'SMS enrollment includes rate limiting to prevent abuse. If the rate limit is exceeded, the endpoint returns HTTP 429 (Too Many Requests).
Email Enrollment Flow
# Step 1: Initiate email enrollment (uses registered email)
curl -X POST https://api.matih.ai/api/v1/mfa/email/enroll \
-H "Authorization: Bearer {accessToken}"
# Step 2: Verify with received code
curl -X POST https://api.matih.ai/api/v1/mfa/email/verify \
-H "Authorization: Bearer {accessToken}" \
-H "Content-Type: application/json" \
-d '{ "code": "345678" }'MFA Verification During Login
After a login that triggers an MFA challenge, the client completes authentication:
curl -X POST https://api.matih.ai/api/v1/auth/mfa/verify \
-H "Content-Type: application/json" \
-d '{
"challengeId": "ch_abc123def456",
"code": "123456",
"method": "TOTP"
}'The response is an AuthResponse with the access and refresh tokens, identical to the standard login response.
Backup Code Usage
Backup codes can be used in place of any MFA method:
curl -X POST https://api.matih.ai/api/v1/auth/mfa/verify \
-H "Content-Type: application/json" \
-d '{
"challengeId": "ch_abc123def456",
"code": "a1b2c3d4",
"method": "BACKUP_CODE"
}'Each backup code can be used exactly once. Users can check remaining codes and regenerate them:
# Check remaining backup codes
curl https://api.matih.ai/api/v1/mfa/backup-codes/count \
-H "Authorization: Bearer {accessToken}"
# Regenerate backup codes (invalidates all existing codes)
curl -X POST https://api.matih.ai/api/v1/mfa/backup-codes/regenerate \
-H "Authorization: Bearer {accessToken}"MFA Status
Users can query their current MFA configuration:
curl https://api.matih.ai/api/v1/mfa/status \
-H "Authorization: Bearer {accessToken}"{
"totpEnabled": true,
"smsEnabled": false,
"emailEnabled": true,
"remainingBackupCodes": 4,
"lastVerified": "2026-02-10T14:30:00Z"
}Token Refresh
Access tokens have a short lifetime (15 minutes by default) for security. Clients use the refresh token to obtain new access tokens without re-authenticating:
curl -X POST https://api.matih.ai/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "eyJhbGciOiJIUzI1NiJ9..." }'The refresh flow:
- The client sends the refresh token to
/api/v1/auth/refresh. - The server validates the refresh token (signature, issuer, expiration, type).
- If valid, a new access token is generated with current roles and tenant context.
- The response includes the new access token and its expiration time.
Refresh tokens are validated specifically as refresh tokens -- an access token submitted to the refresh endpoint will be rejected with "Token is not a refresh token".
Logout
Logout revokes the refresh token, preventing new access tokens from being issued:
curl -X POST https://api.matih.ai/api/v1/auth/logout \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "eyJhbGciOiJIUzI1NiJ9..." }'The existing access token remains valid until it expires (up to 15 minutes). For immediate revocation, the platform maintains a token blocklist in Redis.
User Registration
New users register through the /api/v1/auth/register endpoint:
curl -X POST https://api.matih.ai/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "newuser@acme.com",
"password": "SecureP@ssw0rd!",
"firstName": "Jane",
"lastName": "Doe",
"tenantId": "acme-corp"
}'After registration, the user receives a verification email. Email verification is completed via:
curl -X POST https://api.matih.ai/api/v1/auth/verify-email \
-H "Content-Type: application/json" \
-d '{ "code": "verification-code-from-email" }'API Key Authentication
For programmatic access (CI/CD pipelines, external integrations, automation scripts), the platform provides API key authentication managed by the ApiKeyController at /api/v1/api-keys.
Creating an API Key
curl -X POST https://api.matih.ai/api/v1/api-keys \
-H "Authorization: Bearer {accessToken}" \
-H "Content-Type: application/json" \
-d '{
"name": "CI Pipeline Key",
"description": "Used by GitHub Actions for deployment",
"scopes": ["queries:execute", "data:read"],
"expirationDays": 90,
"ipWhitelist": ["203.0.113.0/24"],
"rateLimit": 1000
}'The response includes the full API key, which is only shown once:
{
"keyId": 42,
"name": "CI Pipeline Key",
"apiKey": "mk_live_abc123def456...",
"prefix": "mk_live_abc1",
"scopes": ["queries:execute", "data:read"],
"expiresAt": "2026-05-13T00:00:00Z"
}API Key Features
| Feature | Description |
|---|---|
| Scoped permissions | Each key has a specific set of allowed scopes |
| IP whitelisting | Restrict key usage to specific IP ranges |
| Rate limiting | Per-key rate limits for request throttling |
| Key rotation | Zero-downtime rotation with grace period overlap |
| Test/Live modes | Separate keys for test and production environments |
| Expiration | Configurable expiration with rotation recommendations |
API Key Rotation
Rotation creates a new key while keeping the old key active during a grace period:
# Initiate rotation (both keys valid during grace period)
curl -X POST https://api.matih.ai/api/v1/api-keys/42/rotate \
-H "Authorization: Bearer {accessToken}"
# Check rotation status
curl https://api.matih.ai/api/v1/api-keys/42/rotation-status \
-H "Authorization: Bearer {accessToken}"
# Complete rotation immediately (revoke old key)
curl -X POST https://api.matih.ai/api/v1/api-keys/42/rotation/complete \
-H "Authorization: Bearer {accessToken}"Using an API Key
API keys are passed via the ApiKeyAuthFilter or the ApiKeyAuthenticationFilter:
curl https://api.matih.ai/api/v1/queries/execute \
-H "X-API-Key: mk_live_abc123def456..."Service-to-Service Authentication
Internal services authenticate to each other using short-lived service tokens:
// Generate a service token with specific scopes
String serviceToken = provider.generateServiceToken(
"query-engine",
Set.of("sql:execute", "data:read")
);Service tokens have a 5-minute lifetime, carry the service type claim, and include only the scopes needed for the specific inter-service call. This follows the principle of least privilege.
OAuth2 Integration
The IAM service includes OAuth2 support through dedicated controllers:
| Controller | Endpoint | Purpose |
|---|---|---|
OAuth2AuthorizationController | /api/v1/oauth2/authorize | Authorization code flow |
OAuth2TokenController | /api/v1/oauth2/token | Token exchange |
OAuth2ClientController | /api/v1/oauth2/clients | Client registration and management |
OAuth2 enables integration with external identity providers (Keycloak, Azure AD, Okta) and allows MATIH to act as an OAuth2 provider for downstream applications.
Session Management
The SessionController manages active user sessions:
# List active sessions
curl https://api.matih.ai/api/v1/sessions \
-H "Authorization: Bearer {accessToken}"
# Revoke a specific session
curl -X DELETE https://api.matih.ai/api/v1/sessions/{sessionId} \
-H "Authorization: Bearer {accessToken}"Session tracking includes device information, IP address, and user agent for security monitoring. The LoginAnomalyDetector service flags suspicious login patterns based on geolocation and behavioral analysis.
Security Headers and Request Tracking
Every authentication request captures:
| Header | Purpose |
|---|---|
User-Agent | Device and browser identification |
X-Forwarded-For | Client IP address (behind proxies) |
X-Real-IP | Direct client IP address |
These values are passed to the AuthenticationService for anomaly detection, session tracking, and audit logging. The GeoLocationService resolves IP addresses to geographic locations for security analysis.
Next Steps
With authentication understood, proceed to Authorization and RBAC to learn how the platform determines what authenticated users are allowed to do.