MATIH Platform is in active MVP development. Documentation reflects current implementation status.
6. Identity & Access Management
IAM Architecture

IAM Service Architecture

Production - Spring Boot 3.2, Java 21, PostgreSQL, Redis

The IAM service (iam-service) is a Spring Boot 3.2 application running on Java 21 that serves as the centralized identity provider for the MATIH platform. It manages user identities, authentication flows, authorization policies, and session state for every tenant in the system.


6.1High-Level Architecture

The IAM service follows a layered architecture pattern with clear separation of concerns:

                    +---------------------+
                    |    API Gateway       |
                    |    (Port 8080)       |
                    +----------+----------+
                               |
                    +----------v----------+
                    |   IAM Service        |
                    |   (Port 8081)        |
                    |                      |
                    | +------------------+ |
                    | | REST Controllers | |
                    | | (16 controllers) | |
                    | +--------+---------+ |
                    |          |           |
                    | +--------v---------+ |
                    | | Business Services| |
                    | | (23 services)    | |
                    | +--------+---------+ |
                    |          |           |
                    | +--------v---------+ |
                    | | Security Layer   | |
                    | | JWT / Filters    | |
                    | +--------+---------+ |
                    |          |           |
                    | +--------v---------+ |
                    | | JPA Repositories | |
                    | | (25 repos)       | |
                    | +--------+---------+ |
                    +----------+-----------+
                               |
              +----------------+----------------+
              |                                 |
    +---------v---------+            +----------v----------+
    |   PostgreSQL      |            |      Redis          |
    |   (users, roles,  |            |  (sessions,         |
    |    tokens, keys)  |            |   permission cache) |
    +-------------------+            +---------------------+

Service Overview

PropertyValue
Artifactcom.matih:iam-service
FrameworkSpring Boot 3.2
LanguageJava 21
Port8081
DatabasePostgreSQL via Spring Data JPA
CacheRedis via Spring Cache
SecuritySpring Security 6 with custom filter chain
Token LibraryJJWT 0.12.3
Policy EngineOpen Policy Agent (OPA)
API DocsSpringDoc OpenAPI 3.0

Package Structure

The service is organized into clearly separated packages:

com.matih.iam/
  config/           # Spring configuration classes
  controller/       # REST API controllers (16 controllers)
  dto/
    request/        # Inbound request DTOs (26 DTOs)
    response/       # Outbound response DTOs (20 DTOs)
  entity/           # JPA entity models (25 entities)
  exception/        # Custom exception types and global handler
  keycloak/         # Keycloak SSO integration
  mapper/           # Entity-to-DTO mappers
  opa/              # Open Policy Agent client
  pbac/             # Policy-based access control
  ratelimit/        # Rate limiting configuration
  repository/       # Spring Data JPA repositories (25 repos)
  scim/             # SCIM 2.0 protocol implementation
  secrets/          # Secrets management integration
  security/         # JWT provider, filters, and auth logic
  service/          # Business logic services (23 services)
  sms/              # SMS provider abstraction (Twilio)
  sso/              # SSO provider configuration (OAuth, SAML)
  zerotrust/        # Zero-trust continuous verification

6.2Domain Model

Core Entities

EntityTableDescription
UserusersCore user identity with email, password hash, tenant association, and status
RolerolesNamed role with a set of permissions, supports parent-child inheritance
UserRoleuser_rolesMany-to-many mapping between users and roles
PermissionpermissionsFine-grained permission in resource:action format
PasswordPolicypassword_policiesPer-tenant password complexity, rotation, and history rules
PasswordHistorypassword_historyPrevious password hashes to prevent reuse

Authentication Entities

EntityTableDescription
EmailVerificationTokenemail_verification_tokensTime-limited token for email verification
RefreshTokenrefresh_tokensOpaque refresh token with token family for rotation detection
LoginHistorylogin_historyAudit trail of login attempts with IP, user agent, and outcome
UserDeviceuser_devicesRegistered devices with fingerprint, OS, browser, and trust status
UserSessionuser_sessionsActive session records with creation time and token binding

MFA Entities

EntityTableDescription
UserMfaCredentialuser_mfa_credentialsTOTP secret, SMS phone number, or email MFA per user
MfaChallengemfa_challengesPending MFA challenge with expiration and attempt counter
MfaRecoveryTokenmfa_recovery_tokensRecovery tokens for MFA bypass
UserBackupCodeuser_backup_codesOne-time backup codes generated during enrollment
MfaPolicymfa_policiesTenant-level MFA enforcement rules
MfaBypassHistorymfa_bypass_historyAudit trail of MFA bypass events

OAuth2 Entities

EntityTableDescription
OAuth2Clientoauth2_clientsRegistered OAuth2 client applications
OAuth2AuthorizationCodeoauth2_authorization_codesShort-lived authorization codes
OAuth2AccessTokenoauth2_access_tokensIssued access tokens with scope and revocation status

Administrative Entities

EntityTableDescription
ImpersonationSessionimpersonation_sessionsAdmin impersonation sessions with audit trail
ApiKeyapi_keysLong-lived API keys with scoped permissions
AccessRequestaccess_requestsSelf-service access request workflow
AccessRequestAuditLogaccess_request_audit_logsAudit trail for access request decisions

User Entity Detail

The User entity is the central domain object supporting multi-tenancy and social login:

@Entity
@Table(name = "users")
@SQLRestriction("deleted = false")
public class User {
    private Long id;
    private UUID tenantId;
    private String email;
    private String passwordHash;
    private String firstName;
    private String lastName;
    private String displayName;
    private String phoneNumber;
    private boolean enabled;
    private boolean locked;
    private boolean emailVerified;
    private boolean mfaEnabled;
    private int failedLoginAttempts;
    private Instant lockedUntil;
    private Instant lastLoginAt;
    private String lastLoginIp;
    private String authProvider;           // password, google, microsoft, github
    private String googleSubjectId;
    private String microsoftSubjectId;
    private String githubSubjectId;
    private Set<Role> roles;
    private Set<UUID> groupIds;            // JSONB for MFA policy targeting
    private boolean deleted;
    private Instant deletedAt;
}

Key design decisions:

  • Soft delete via @SQLRestriction("deleted = false") -- deleted users are filtered at the JPA level
  • Optimistic locking via @Version to prevent concurrent update conflicts
  • JPA Auditing via @CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy
  • Unique constraint on (tenant_id, email) for multi-tenant email uniqueness
  • Social login support via dedicated subject ID columns per OAuth provider

Entity Relationship Diagram

User ----< UserRole >---- Role ----< RolePermission >---- Permission
  |                         |
  |----< UserSession        |----< ChildRole (self-referencing)
  |----< UserDevice
  |----< UserMfaCredential
  |----< UserBackupCode
  |----< RefreshToken
  |----< ApiKey
  |----< LoginHistory
  |----< PasswordHistory
  |----< AccessRequest
  |----< ImpersonationSession (as admin or target)
  |----< EmailVerificationToken

6.3Security Architecture

Filter Chain

Request
  |
  v
[CORS Filter]
  |
  v
[Rate Limiting Filter]
  |
  v
[API Key Authentication Filter]  -- For service-to-service calls
  |
  v
[JWT Authentication Filter]      -- For user-facing calls
  |
  v
[Scope Authorization Aspect]     -- @RequiresScope annotation check
  |
  v
[Controller Method]

JWT Token Provider

The JwtTokenProvider uses JJWT 0.12.3 with HS256 signing and generates four distinct token types:

@Component
public class JwtTokenProvider {
    public String generateAccessToken(User user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("user_id", user.getId());
        claims.put("tenant_id", user.getTenantId());
        claims.put("roles", new ArrayList<>(user.getAuthorities()));
 
        return Jwts.builder()
            .claims(claims)
            .subject(user.getEmail())
            .issuer(securityProperties.getJwt().getIssuer())
            .id(UUID.randomUUID().toString())
            .issuedAt(new Date())
            .expiration(expiryDate)
            .signWith(secretKey, Jwts.SIG.HS256)
            .compact();
    }
}

Token Types

Token TypeUse CaseSubjectKey Claims
Access TokenUser authenticationUser emailuser_id, tenant_id, roles
Refresh TokenToken renewalUser emailtype: "refresh"
OAuth2 Access TokenOAuth2 flowsUser email or client IDclient_id, scope, tenant_id
Impersonation TokenAdmin troubleshootingTarget user emailimpersonation: true, impersonator_id, impersonation_session_id

JWT Claims Structure

ClaimTypeDescription
subStringUser email address
user_idLongInternal user ID
tenant_idUUIDTenant identifier
rolesList<String>Authorities (ROLE_ADMIN, users:read, etc.)
issStringToken issuer
audStringToken audience
jtiStringUnique token ID (UUID)
iatLongIssued at timestamp
expLongExpiration timestamp

6.4Multi-Tenancy

Tenant isolation is enforced at every layer:

Controller Layer: Most endpoints require an X-Tenant-ID header:

public ResponseEntity<UserResponse> createUser(
    @RequestHeader("X-Tenant-ID") UUID tenantId,
    @Valid @RequestBody CreateUserRequest request) {
    UserResponse response = userService.createUser(tenantId, request);
    return ResponseEntity.status(HttpStatus.CREATED).body(response);
}

Repository Layer: JPA queries filter by tenant_id:

Optional<User> findByTenantIdAndEmail(UUID tenantId, String email);
Page<User> findByTenantId(UUID tenantId, Pageable pageable);

Token Layer: The tenant_id claim in JWT tokens ensures downstream services verify tenant context without additional database lookups.


6.5Integration Points

Inbound Dependencies

ConsumerProtocolPurpose
API GatewayHTTPToken validation, routing
Tenant ServiceHTTPUser provisioning
Frontend applicationsHTTPLogin, registration, MFA
Data plane servicesJWTToken validation via shared library

Outbound Dependencies

DependencyProtocolPurpose
PostgreSQLJDBCPersistent storage
RedisRedisSession and permission cache
KeycloakOIDC/HTTPSSO federation
OPAHTTPPolicy evaluation
Notification ServiceHTTPEmail verification, alerts
TwilioHTTPSSMS MFA delivery

6.6Error Handling

The GlobalExceptionHandler maps exceptions to structured error responses:

ExceptionHTTP StatusError Code
AuthenticationException401AUTHENTICATION_FAILED
TokenException401TOKEN_INVALID
ResourceNotFoundException404RESOURCE_NOT_FOUND
BusinessException400BUSINESS_RULE_VIOLATION
DuplicateResourceException409RESOURCE_DUPLICATE
{
  "timestamp": "2026-02-12T10:30:00Z",
  "status": 401,
  "code": "AUTHENTICATION_FAILED",
  "message": "Invalid credentials",
  "path": "/api/v1/auth/login"
}

6.7Configuration

matih:
  security:
    jwt:
      secret-key: ${JWT_SECRET_KEY}
      access-token-expiration: 900000          # 15 minutes
      refresh-token-expiration: 604800000      # 7 days
      issuer: "matih-platform"
      audience: "matih-api"
    lockout:
      max-attempts: 5
      lockout-duration-minutes: 30
    mfa:
      totp-issuer: "MATIH Platform"
      challenge-expiration: 300
      backup-code-count: 10

6.8Health and Observability

EndpointPurpose
GET /healthLiveness probe
GET /health/readyReadiness probe
GET /actuator/prometheusPrometheus metrics

Key metrics: iam_login_total, iam_mfa_challenges_total, iam_token_issued_total, iam_active_sessions, iam_permission_cache_hit_ratio.


Next Steps