Skip to content

Authentication & Authorization Architecture – Portugal Odyssey

Overview

We adopt 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. This satisfies OAuth2/OIDC, JWT, RBAC, multi-tenant needs, social login, password policies, 2FA, and session SSO across subdomains.

  • 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
  • Frontends: Public, Admin, Partner Console integrate via PKCE authorization code flow
  • Gateways/Services: Validate JWT via JWKS + enforce RBAC/tenant guards

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

High-Level Flows

  1. Login (PKCE, Authorization Code):

  2. Frontend redirects to Keycloak → user authenticates (password policy + 2FA/WebAuthn)

  3. Keycloak issues code → frontend exchanges via backend or directly (PKCE) for access_token + refresh_token.
  4. Frontend stores access token in memory; refresh token via secure HTTP-only cookie (optional) on auth-service domain to centralize refresh.

  5. Social Login:

  6. Keycloak configured with Google/Facebook IdPs.

  7. After login, tokens carry normal roles/tenant claims.

  8. API Requests:

  9. Clients send Authorization: Bearer <access_token> to API Gateway → gateway middleware validates JWT via JWKS (cached in Redis) and injects x-user, x-tenant headers for services.

  10. Services:

  11. NestJS guards validate tenant membership and RBAC, using token claims and, when needed, platform DB lookups.

  12. Session Logout:

  13. Frontend calls auth-service /auth/logout → revokes Keycloak refresh token (introspection/revocation endpoint) and clears cookies. Keycloak Single Logout propagates to clients.

Components

  • Keycloak

  • Realm: portugal-odyssey

  • Clients: public-app, admin-app, partner-console, auth-service, api-gateway
  • Identity Providers: Google, Facebook
  • Policies: Password history/strength; OTP, WebAuthn
  • Mappers: tenant_id, tenant_ids, preferred_username, email_verified

  • Auth-Service (NestJS)

  • REST: login helpers, refresh, logout, user profile patch, tenant invites

  • GraphQL: rich profile + tenant graph queries
  • RBAC: guards based on roles and permissions
  • Tenancy: guards using tenant_id claim + DB check
  • Integrations: Keycloak admin API (KC Admin client) for user provisioning and group management

  • Shared Validation

  • Express middleware for API Gateway
  • NestJS AuthGuard + RolesGuard + TenantGuard for services
  • JWKS retrieval with caching and rotation handling

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

Alternative (Ory Hydra + Ory Kratos)

If Keycloak is not desired, equivalent setup with Hydra (OAuth2) and Kratos (identity) is supported. The NestJS guards and gateway middleware remain identical; token issuer/metadata and admin integration differ. See ory-notes.md for migration notes (to be added upon request).