Skip to content

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 (iam schema)
  • 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, admin at realm level; optional client roles per application.
  • Tokens include:
  • realm_access.roles for global roles
  • resource_access for client roles
  • Custom claim tenant_id (single) and tenant_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:

{
  "refresh_token": "string" // optional if using cookie
}

Response: 200 OK

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "expires_in": 900
}

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:

{
  "display_name": "John Updated",
  "phone": "+351912345678"
}

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:

{
  "email": "newuser@example.com",
  "role": "partner" // or "admin"
}

Response: 202 Accepted

POST /tenants/{tenantId}/members/{userId}/roles

Assign a role to a tenant member.

Request:

{
  "role": "partner"
}

Response: 204 No Content

Authentication Flows

1. Login (PKCE, Authorization Code)

  1. Frontend redirects to Keycloak → user authenticates (password policy + 2FA/WebAuthn)
  2. Keycloak issues code → frontend exchanges via backend or directly (PKCE) for access_token + refresh_token.
  3. Frontend stores access token in memory; refresh token via secure HTTP-only cookie (optional) on auth-service domain to centralize refresh.

2. Social Login

  • Keycloak configured with Google/Facebook IdPs.
  • After login, tokens carry normal roles/tenant claims.

3. API Requests

  1. Clients send Authorization: Bearer <access_token> to API Gateway
  2. Gateway middleware validates JWT via JWKS (cached in Redis)
  3. Gateway injects x-user, x-tenant headers 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)

  1. Use OIDC Authorization Code with PKCE against Keycloak realm client (public-app, admin-app, partner-console).
  2. Set redirect_uri per app domain.
  3. Store access token in memory. For refresh, call POST /auth/refresh on auth-service which stores/rotates secure HTTP-only cookie on auth(-env).portugalodyssey.pt.
  4. Configure CORS to allow cross-site cookies: SameSite=None; Secure.

API Gateway

  1. Add Express middleware (provided under services/auth-service/src/shared/express-jwt.ts) to validate JWT from Authorization header.
  2. On success, set proxy headers:
  3. x-user-id, x-email, x-tenant-id, x-roles
  4. Forward only to internal services; strip authorization from logs.

Backend Services (NestJS)

Use shared Nest guards from services/auth-service/src/shared/nest/:

  • JwtAuthGuard ensures authentication
  • RolesGuard('admin'|'partner'|'customer')
  • TenantGuard() enforces tenant claim present and (optionally) membership check via DB

Example controller:

@UseGuards(JwtAuthGuard, RolesGuard('admin'))
@Get('/tenants/:id')
findTenant() { ... }

Messaging (RabbitMQ)

When emitting events, include user context metadata: - user_id, tenant_id, roles, trace_id

WebSockets

  1. Validate JWT on connection (Authorization: Bearer ... in query/header).
  2. Create per-user rooms keyed by sub and optional tenant_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