Life ConnectLife Connect
Wiki index
Architecture
Services
Concepts
Runbooks
Infra
Swagger Docs
GitHub
Wiki index
Architecture
Services
Concepts
Runbooks
Infra
Swagger Docs
GitHub
  • Architecture

    • Architecture overview
    • Service-to-service communication
    • Data layer
    • Authentication
    • ADB monorepo audit — use cases & flow maps
Last updated 2026-05-21

Authentication & authorization

AI-generated content

This document was generated by an AI assistant. Verify accuracy before relying on the details.

ADB uses Keycloak as its single identity provider (SSO + OAuth2). All Java services are OAuth2 resource servers and validate JWTs themselves.

Topology

flowchart LR
    User[User]
    UI[adb-ui<br/>or<br/>adb-web/ui]
    KC[Keycloak<br/>ADB realm]
    BFF[adb-web/bff]
    GW[adb/proxy<br/>Nginx]
    Svc[Java microservice]

    User -->|1. login| UI
    UI -->|2. redirect| KC
    KC -->|3. auth flow| User
    User -->|4. credentials| KC
    KC -->|5. JWT| UI
    UI -->|6. Bearer JWT| BFF
    BFF -->|7. validate JWT| KC
    BFF -->|8. forward JWT| GW
    GW --> Svc
    Svc -->|validate JWT<br/>via JWKS| KC

Keycloak realm

  • URL: https://dev-security.life-connect.fr/realms/ADB (dev)
  • Issuer URI propagated through the SECURITY_ISSUER_URI variable
  • Token endpoint: SECURITY_TOKEN_URI

OAuth2 clients

ClientTypeUsage
Frontend (UI)publicAuthorization Code flow + PKCE
Service-to-serviceconfidentialClient Credentials flow (each service has its own CLIENT_ID / CLIENT_SECRET)
Admin clientconfidentialUser provisioning from adb-persons (variables ADMIN_CLIENT_ID / ADMIN_CLIENT_SECRET)

Frontend flow

adb-ui (legacy Angular)

Uses keycloak-angular (v19.0.2) + keycloak-js (v22.0.5). Configuration lives in src/environments/environment.*.ts:

keycloak: {
  issuer: '...',
  realm: 'ADB',
  clientId: '...',
  redirectUri: '...',
}

An AuthGuard protects the routes; the JWT is automatically attached to outgoing requests through an HttpInterceptor.

adb-web/apps/bff

The Hono BFF (still being built out) must:

  1. Validate the JWT at the edge (Keycloak public key via JWKS).
  2. Extract the user and their roles.
  3. Forward the request to the Java backend, preserving the Authorization: Bearer ... header (so the Java service runs its own validation, defense in depth).

Cloudflare secret: JWT_SECRET (TODO on the wrangler.jsonc side).

Backend flow

Each Spring service declares the spring-boot-starter-oauth2-resource-server starter and configures:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: ${SECURITY_ISSUER_URI}

Verification is stateless: no per-request call to Keycloak, only the JWKS fetched at startup.

User provisioning

adb-persons is the only service that writes to Keycloak (account creation, role management via InvitationController). It uses an admin client with the realm-management scope.

Per-service environment variables

VariableRole
SECURITY_ISSUER_URIKeycloak issuer (JWT validation)
SECURITY_TOKEN_URIToken endpoint (Client Credentials for service-to-service calls)
CLIENT_ID / CLIENT_SECRETService credentials (for outbound calls to other services)
ADMIN_CLIENT_ID / ADMIN_CLIENT_SECRET(adb-persons only) Keycloak provisioning

Kubernetes charts / secrets

Credentials are injected via adb-charts/charts/security/ (legacy UAA + OAuth scopes) and per-environment Kubernetes Secret resources (values-dev.yaml, values-prod.yaml, etc.).

Roles and scopes

Authorization is role-based. Each Keycloak role maps to a set of scopes in ScopesByRoles (adb-common). The table below lists all roles and their purpose:

RoleDescription
agentStandard agency user — full CRUD on persons, contracts, parts, files
agent-adminAgent with user-management rights (users.write, users.delete)
tenantTenant — read-only on own contracts, file uploads
technicianField technician — limited read (parts list, supplier list, tickets)
accountant.readAccountant read-only — accounting events, ledger entries
accountant.writeAccountant with write rights
application-adminPlatform admin — organization management
salesSales user — persons, parts, notes, tickets
assetAsset manager — read/write across contracts, parts, accounting, financial
internalService-to-service — full internal access; never assigned to human users
openUnauthenticated-accessible endpoints (registration, invitations)

Scopes are checked per-service. For example, agent grants persons.list, contracts.write, files.read etc., but only within the services that map it. The authoritative source is ScopesByRoles.java in adb-common.

Delegation (adb-security)

The adb-security shared module (added in v0.3.108) adds a delegation layer on top of the role-based check. A Delegation record lets one entity (the delegatee) act on behalf of another on a specific resource, with a restricted set of scopes and an optional validity window.

Delegatee ──(hasAccess?)──► DelegationService
                                  │
                         DelegationPort.findByDelegateeAndResource(ctx, requester, resource)
                                  │
                         filter: isValid(d) && d.delegatedScopes.contains(requiredScope)
                                  │
                         Mono<Boolean>

Services that need delegation:

  1. Add adb-security as a Maven dependency.
  2. Implement DelegationPort (reactive MongoDB repository).
  3. Inject DelegationService and call hasAccess(contextData, resource, requester, scope) in controller or service layer.

Notes

  • No server-side session: everything is stateless; the JWT carries the identity and the roles.
  • Refresh token: handled by keycloak-js in the browser.
  • Logout: call to Keycloak's end_session_endpoint plus token invalidation on the UI side.
Edit this page
Last Updated:
Contributors: Yevhenii Khudolii
Prev
Data layer
Next
ADB monorepo audit — use cases & flow maps