MATIH Platform is in active MVP development. Documentation reflects current implementation status.
3. Security & Multi-Tenancy
Authentication

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:

ComponentLocationResponsibility
Commons JWT Librarycommons/commons-java/.../security/authentication/Token generation, validation, and claim extraction
IAM Servicecontrol-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 TypePurposeDefault LifetimeClaim: type
Access TokenAuthenticate API requests15 minutesaccess
Refresh TokenObtain new access tokens7 daysrefresh
Service TokenInter-service communication5 minutesservice
API Key TokenLong-lived programmatic accessConfigurableapi_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"]
}
ClaimDescription
jtiUnique token identifier (UUID) for revocation tracking
subUser identifier (subject)
issToken issuer, verified during validation
iatIssued-at timestamp
expExpiration timestamp
typeToken type discriminator
tenant_idThe tenant to which this user belongs
rolesSet 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:

FailureError 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:

MethodServiceDescription
TOTPMfaServiceTime-based one-time passwords via authenticator apps (Google Authenticator, Authy)
SMSSmsMfaServiceVerification codes sent via SMS to a registered phone number
EmailEmailMfaServiceVerification 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:

  1. The client sends the refresh token to /api/v1/auth/refresh.
  2. The server validates the refresh token (signature, issuer, expiration, type).
  3. If valid, a new access token is generated with current roles and tenant context.
  4. 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

FeatureDescription
Scoped permissionsEach key has a specific set of allowed scopes
IP whitelistingRestrict key usage to specific IP ranges
Rate limitingPer-key rate limits for request throttling
Key rotationZero-downtime rotation with grace period overlap
Test/Live modesSeparate keys for test and production environments
ExpirationConfigurable 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:

ControllerEndpointPurpose
OAuth2AuthorizationController/api/v1/oauth2/authorizeAuthorization code flow
OAuth2TokenController/api/v1/oauth2/tokenToken exchange
OAuth2ClientController/api/v1/oauth2/clientsClient 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:

HeaderPurpose
User-AgentDevice and browser identification
X-Forwarded-ForClient IP address (behind proxies)
X-Real-IPDirect 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.