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

User Registration

User registration is the entry point for new identities in the MATIH platform. The IAM service supports two registration paths: self-service registration through the AuthController REST API, and enterprise provisioning through the SCIM 2.0 protocol. Both paths converge on the same user entity model and apply identical password policies and email verification requirements.


Registration Flow Overview

The self-service registration flow follows a multi-step process that creates the user account, issues initial tokens, and triggers asynchronous email verification:

Client                   IAM Service                  Database              Notification Service
  |                          |                            |                        |
  |  POST /api/v1/auth/register                           |                        |
  |------------------------->|                            |                        |
  |                          |  Validate request          |                        |
  |                          |  Check email uniqueness    |                        |
  |                          |-------------------------->|                        |
  |                          |  Hash password (BCrypt)    |                        |
  |                          |  Create User entity        |                        |
  |                          |-------------------------->|                        |
  |                          |  Generate verification token                       |
  |                          |-------------------------->|                        |
  |                          |  Generate access + refresh tokens                  |
  |                          |  Create session            |                        |
  |                          |-------------------------->|                        |
  |                          |  Send verification email   |                        |
  |                          |----------------------------------------------->|   |
  |  200 OK (AuthResponse)   |                            |                        |
  |<-------------------------|                            |                        |

Registration Request

The RegisterRequest DTO defines the fields required for self-service registration:

public class RegisterRequest {
    @NotBlank(message = "Email is required")
    @Email(message = "Invalid email format")
    private String email;
 
    @NotBlank(message = "Password is required")
    @Size(min = 8, max = 128, message = "Password must be between 8 and 128 characters")
    private String password;
 
    @NotBlank(message = "First name is required")
    @Size(max = 100)
    private String firstName;
 
    @NotBlank(message = "Last name is required")
    @Size(max = 100)
    private String lastName;
 
    private String organizationName;   // Optional: for tenant creation
    private String inviteCode;         // Optional: for joining existing tenant
}

API Endpoint

POST /api/v1/auth/register
Content-Type: application/json

Request body:

{
  "email": "jane.doe@acme.com",
  "password": "SecureP@ssw0rd!",
  "firstName": "Jane",
  "lastName": "Doe",
  "organizationName": "Acme Corporation"
}

Success response (200):

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  "tokenType": "Bearer",
  "expiresIn": 900,
  "user": {
    "id": 42,
    "email": "jane.doe@acme.com",
    "firstName": "Jane",
    "lastName": "Doe",
    "emailVerified": false,
    "mfaEnabled": false,
    "roles": ["USER"]
  }
}

Error responses:

StatusConditionBody
400Validation failure{ "code": "VALIDATION_ERROR", "errors": [...] }
400Email already registered{ "code": "RESOURCE_DUPLICATE", "message": "Email already exists" }
429Rate limit exceeded{ "code": "RATE_LIMITED", "retryAfter": 60 }

Password Policy Enforcement

Before persisting the user, the PasswordPolicyService validates the password against the active policy. Password policies are configurable per tenant:

RuleDefaultDescription
Minimum length8Minimum number of characters
Maximum length128Maximum number of characters
Require uppercasetrueAt least one uppercase letter
Require lowercasetrueAt least one lowercase letter
Require digittrueAt least one numeric digit
Require special charactertrueAt least one of !@#$%^&*()_+-=
Prevent common passwordstrueChecked against a dictionary of 100,000 common passwords
History check5Cannot reuse the last N passwords
Maximum age (days)90Password must be changed within this period

The password is hashed using BCrypt with a work factor of 12 before storage. The raw password is never logged or persisted.

// Password hashing in AuthenticationService
String hashedPassword = passwordEncoder.encode(request.getPassword());
user.setPassword(hashedPassword);

Email Verification

After registration, the system generates a time-limited verification token and sends it to the user's email address. The user must verify their email before accessing certain platform features.

Verification Token

PropertyValue
Token format6-digit numeric code
Token lifetime24 hours
Storageemail_verification_tokens table
DeliveryEmail via Notification Service

Verify Email Endpoint

POST /api/v1/auth/verify-email
Content-Type: application/json
{
  "email": "jane.doe@acme.com",
  "code": "482916"
}

Success response (200): Empty body

Error responses:

StatusCondition
400Invalid or expired verification code
404User not found

Resend Verification

If the user does not receive the verification email, they can request a new code:

POST /api/v1/auth/resend-verification
Content-Type: application/json
{
  "email": "jane.doe@acme.com"
}

This endpoint is rate-limited to prevent abuse. A maximum of 3 resend requests are allowed within a 15-minute window.


SCIM Provisioning

For enterprise customers integrating MATIH with their identity provider (Okta, Azure AD, OneLogin), the IAM service implements the SCIM 2.0 protocol for automated user lifecycle management.

SCIM Endpoints

MethodEndpointDescription
GET/scim/v2/UsersList users with filtering and pagination
GET/scim/v2/Users/{id}Get a single user
POST/scim/v2/UsersCreate a new user
PUT/scim/v2/Users/{id}Full update of a user
PATCH/scim/v2/Users/{id}Partial update (e.g., deactivate)
DELETE/scim/v2/Users/{id}Delete a user

SCIM User Schema

The ScimUserService maps between the SCIM User schema and the MATIH User entity:

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "userName": "jane.doe@acme.com",
  "name": {
    "givenName": "Jane",
    "familyName": "Doe"
  },
  "emails": [
    {
      "value": "jane.doe@acme.com",
      "type": "work",
      "primary": true
    }
  ],
  "active": true,
  "externalId": "okta-12345"
}

SCIM Filtering

The SCIM implementation supports the standard filter syntax for user queries:

GET /scim/v2/Users?filter=userName eq "jane.doe@acme.com"
GET /scim/v2/Users?filter=active eq true&startIndex=1&count=25

SCIM Authentication

SCIM endpoints are authenticated using a bearer token issued specifically for SCIM clients. This token carries the scim:manage scope and is bound to the tenant that provisioned the SCIM integration.


Invite-Based Registration

When a user is invited to join an existing tenant, the registration flow includes an invite code that links the new user to the correct tenant and assigns initial roles:

{
  "email": "bob@acme.com",
  "password": "SecureP@ssw0rd!",
  "firstName": "Bob",
  "lastName": "Smith",
  "inviteCode": "INV-abc123def456"
}

The invite code is validated against the access_requests table. If valid, the user is created within the inviting tenant's context and assigned the roles specified in the invitation.


Account Lockout

The AccountLockoutService protects against registration abuse and brute-force attacks:

ParameterDefaultDescription
Max registration attempts per IP10 per hourPrevents mass account creation from a single source
Max verification attempts per email5 per hourPrevents verification code brute-forcing
Lockout duration30 minutesTime before a locked account can retry

When an account is locked, subsequent registration or verification attempts return a 423 Locked response with a Retry-After header.


Keycloak User Synchronization

For deployments that use Keycloak as the primary identity provider, the KeycloakUserSyncService synchronizes user records between Keycloak and the MATIH IAM database:

Keycloak                IAM Service
  |                         |
  |  User created/updated   |
  |  (webhook event)        |
  |------------------------>|
  |                         |  Upsert user in IAM DB
  |                         |  Map Keycloak roles to MATIH roles
  |                         |  Update permission cache
  |                         |

The KeycloakSyncController exposes endpoints for triggering manual synchronization:

POST /api/v1/keycloak/sync          # Full sync
POST /api/v1/keycloak/sync/{userId} # Single user sync

Data Model Summary

The registration flow creates or updates the following entities:

User
  |-- UserRole (default: USER role)
  |-- EmailVerificationToken
  |-- UserSession (initial session)
  |-- RefreshToken (initial refresh token)
  |-- PasswordHistory (initial entry)
  |-- LoginHistory (registration event)

Next Steps

After understanding the registration flow, proceed to: