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_URIvariable - Token endpoint:
SECURITY_TOKEN_URI
OAuth2 clients
| Client | Type | Usage |
|---|---|---|
| Frontend (UI) | public | Authorization Code flow + PKCE |
| Service-to-service | confidential | Client Credentials flow (each service has its own CLIENT_ID / CLIENT_SECRET) |
| Admin client | confidential | User 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:
- Validate the JWT at the edge (Keycloak public key via JWKS).
- Extract the user and their roles.
- 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
| Variable | Role |
|---|---|
SECURITY_ISSUER_URI | Keycloak issuer (JWT validation) |
SECURITY_TOKEN_URI | Token endpoint (Client Credentials for service-to-service calls) |
CLIENT_ID / CLIENT_SECRET | Service 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:
| Role | Description |
|---|---|
agent | Standard agency user — full CRUD on persons, contracts, parts, files |
agent-admin | Agent with user-management rights (users.write, users.delete) |
tenant | Tenant — read-only on own contracts, file uploads |
technician | Field technician — limited read (parts list, supplier list, tickets) |
accountant.read | Accountant read-only — accounting events, ledger entries |
accountant.write | Accountant with write rights |
application-admin | Platform admin — organization management |
sales | Sales user — persons, parts, notes, tickets |
asset | Asset manager — read/write across contracts, parts, accounting, financial |
internal | Service-to-service — full internal access; never assigned to human users |
open | Unauthenticated-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:
- Add
adb-securityas a Maven dependency. - Implement
DelegationPort(reactive MongoDB repository). - Inject
DelegationServiceand callhasAccess(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-jsin the browser. - Logout: call to Keycloak's
end_session_endpointplus token invalidation on the UI side.