Authentication & Authorization API¶
Complete guide to authentication and authorization in the Portugal Odyssey platform.
Overview¶
The platform uses a centralized Identity and Access Management (IAM) design based on Keycloak (Open Source OAuth2/OIDC server) and a NestJS-based auth-service facade for platform-specific features.
Key Components¶
- OAuth2/OIDC Provider: Keycloak
- Auth Facade: NestJS
auth-service(TypeScript) - Token Format: OIDC JWT access tokens + refresh tokens
- Cache/Session: Redis (JWKS caching, token blacklists, session mapping)
- User Data: Keycloak DB for identities; platform-specific profile stored in Postgres (
iamschema) - Transport: REST + GraphQL (auth-service) for complex profile/tenant queries
Architecture¶
Domains¶
- Dev:
dev.codecomedy.dev(e.g.,auth-dev.portugalodyssey.pt,sso-dev.portugalodyssey.pt) - Qual:
po.codecomedy.dev(e.g.,auth-qual.portugalodyssey.pt,sso-qual.portugalodyssey.pt) - Prod:
portugalodyssey.pt(e.g.,auth.portugalodyssey.pt,sso.portugalodyssey.pt)
Traefik terminates TLS and routes to Keycloak and the auth-service. Cookies use SameSite=None; Secure for cross-subdomain SSO where applicable.
Tenancy Model¶
Single Keycloak realm: portugal-odyssey.
- Tenants modeled as groups in Keycloak (
tenant:{tenantId}), synced to platform DB. - Roles:
customer,partner,adminat realm level; optional client roles per application. - Tokens include:
realm_access.rolesfor global rolesresource_accessfor client roles- Custom claim
tenant_id(single) andtenant_ids(array) via Keycloak Protocol Mapper
API Endpoints¶
Base URLs¶
- Dev:
https://auth-dev.portugalodyssey.pt - Qual:
https://auth-qual.portugalodyssey.pt - Prod:
https://auth.portugalodyssey.pt
All responses are JSON. Authenticated endpoints require Authorization: Bearer <access_token>.
REST Endpoints¶
POST /auth/refresh¶
Refreshes access token using a secure HTTP-only refresh cookie, or by forwarding a refresh token in the body.
Request:
Response: 200 OK
POST /auth/logout¶
Revokes refresh token at Keycloak and clears cookies.
Response: 204 No Content
GET /auth/session¶
Returns session information, roles and tenants derived from token.
Response: 200 OK
{
"user": {
"id": "uuid",
"email": "user@example.com",
"display_name": "John Doe"
},
"roles": ["customer", "partner"],
"tenant_id": "uuid",
"tenant_ids": ["uuid1", "uuid2"]
}
GET /users/me¶
Returns the current user profile (platform data).
Response: 200 OK
{
"id": "uuid",
"kc_user_id": "keycloak-uuid",
"email": "user@example.com",
"display_name": "John Doe",
"phone": "+351912345678",
"tenants": [...],
"roles": [...]
}
PATCH /users/me¶
Update display_name/phone and other profile fields.
Request:
Response: 200 OK (updated user object)
POST /tenants/{tenantId}/invite¶
Admin/Partner only: invite a user to a tenant (sends email via Notification service).
Request:
Response: 202 Accepted
POST /tenants/{tenantId}/members/{userId}/roles¶
Assign a role to a tenant member.
Request:
Response: 204 No Content
Authentication Flows¶
1. Login (PKCE, Authorization Code)¶
- Frontend redirects to Keycloak → user authenticates (password policy + 2FA/WebAuthn)
- Keycloak issues
code→ frontend exchanges via backend or directly (PKCE) foraccess_token+refresh_token. - Frontend stores access token in memory; refresh token via secure HTTP-only cookie (optional) on
auth-servicedomain to centralize refresh.
2. Social Login¶
- Keycloak configured with Google/Facebook IdPs.
- After login, tokens carry normal roles/tenant claims.
3. API Requests¶
- Clients send
Authorization: Bearer <access_token>to API Gateway - Gateway middleware validates JWT via JWKS (cached in Redis)
- Gateway injects
x-user,x-tenantheaders for services
4. Services¶
- NestJS guards validate tenant membership and RBAC, using token claims and, when needed, platform DB lookups.
5. Session Logout¶
- Frontend calls
auth-service /auth/logout→ revokes Keycloak refresh token (introspection/revocation endpoint) and clears cookies. Keycloak Single Logout propagates to clients.
Integration Guides¶
Frontend (React/Next/Vite)¶
- Use OIDC Authorization Code with PKCE against Keycloak realm client (
public-app,admin-app,partner-console). - Set
redirect_uriper app domain. - Store access token in memory. For refresh, call
POST /auth/refreshonauth-servicewhich stores/rotates secure HTTP-only cookie onauth(-env).portugalodyssey.pt. - Configure CORS to allow cross-site cookies:
SameSite=None; Secure.
API Gateway¶
- Add Express middleware (provided under
services/auth-service/src/shared/express-jwt.ts) to validate JWT fromAuthorizationheader. - On success, set proxy headers:
x-user-id,x-email,x-tenant-id,x-roles- Forward only to internal services; strip authorization from logs.
Backend Services (NestJS)¶
Use shared Nest guards from services/auth-service/src/shared/nest/:
JwtAuthGuardensures authenticationRolesGuard('admin'|'partner'|'customer')TenantGuard()enforces tenant claim present and (optionally) membership check via DB
Example controller:
Messaging (RabbitMQ)¶
When emitting events, include user context metadata:
- user_id, tenant_id, roles, trace_id
WebSockets¶
- Validate JWT on connection (
Authorization: Bearer ...in query/header). - Create per-user rooms keyed by
suband optionaltenant_id.
Keycloak Configuration¶
Configure realm portugal-odyssey, clients, and protocol mappers:
tenant_id(single string)tenant_ids(JSON array)- Map realm roles to token via default mappers
Enable Password Policies and 2FA (OTP/WebAuthn) in realm settings.
Configure Social IdPs: Google, Facebook (client id/secret, redirect URI provided by Keycloak)
Security¶
- HTTPS only; HSTS via Traefik
- PKCE mandatory for public clients
- Access token TTL: 15 minutes; Refresh TTL: 30 days (rotating, reuse detection)
- Rate limiting on auth endpoints; device-based session mapping; suspicious login alerts via Notification service
- Bcrypt for internal secrets; helmet, CSRF where cookies used
Database Schema¶
See Database Schema for the IAM schema structure.
See Also¶
- Auth Service Documentation
- Architecture Overview
- JWT Token Structure
- Security Best Practices