Skip to main content

Architecture Overview

Architecture Overview

This document describes a production-ready authentication and authorization architecture built with:

  • Zitadel as the Identity Provider
  • Google and LINE as external login providers
  • Angular as the frontend application
  • Spring Boot API Gateway as the entry point
  • OpenFGA for relationship-based authorization
  • Cerbos for policy-based access control

The goal is to clearly separate responsibilities between authentication, relationship authorization, and business policy evaluation.


High-Level Architecture Diagram

Authentication Layer

Zitadel

Zitadel acts as the central identity provider:

  • Issues access token and ID token
  • Manages users, organizations, and coarse-grained roles
  • Integrates with external identity providers

Angular and backend services only trust tokens issued by Zitadel.

Google and LINE Login

Google and LINE are configured as external identity providers inside Zitadel.

Key points:

  • Angular never talks directly to Google or LINE
  • All tokens consumed by the system are issued by Zitadel
  • Account linking and user lifecycle remain centralized

This avoids frontend complexity and prevents token trust fragmentation.

Frontend Login Flow

Angular uses OAuth2 Authorization Code Flow with PKCE.

Flow summary:

  1. User clicks login
  2. Angular redirects to Zitadel
  3. User chooses Google or LINE
  4. Zitadel completes authentication
  5. Angular receives access token and ID token

Token usage:

  • access token is used for API calls
  • ID token is used only by the frontend for user information

API Gateway Responsibility

The Spring Boot API Gateway acts as an OAuth2 Resource Server.

Its responsibilities are intentionally limited and strict:

  • Validate JWT signature using Zitadel JWKS
  • Verify issuer, audience, and expiry
  • Reject invalid or expired tokens
  • Do not call Zitadel for introspection per request

Token validation is done locally for performance and reliability.

Relationship Authorization with OpenFGA

OpenFGA is responsible for object-level and relationship-based authorization.

Typical questions handled by OpenFGA:

  • Is the user a member of this organization
  • Is the user the owner of this resource
  • Does the user have editor access to this document

These checks are fast and scale well with large numbers of users and resources.

Relationship data is usually written when:

  • A user joins a project
  • A resource is created
  • Ownership or membership changes

Policy Evaluation with Cerbos

Cerbos handles business rules and conditional access logic.

Examples of policies:

  • Editors can update resources only in active status
  • Access is blocked outside business hours
  • Feature access depends on subscription plan

Cerbos does not store data and does not know about Zitadel or OpenFGA.

It evaluates decisions based on attributes provided by the API Gateway.

Authorization Decision Flow

The API Gateway performs authorization in this order:

  1. Validate access token
  2. Check relationship permission via OpenFGA
  3. Evaluate business policy via Cerbos
  4. Allow or deny the request

This separation keeps each system focused and maintainable.

Why This Separation Works

  • Authentication logic is isolated
  • Relationship complexity does not leak into policies
  • Business rules are not hard-coded in services
  • Each component can evolve independently

This architecture scales well for SaaS platforms and multi-tenant systems.

Notes on Zitadel Roles

Zitadel roles are best used for:

  • High-level access
  • Admin versus normal user
  • UI feature toggling

They should not be used for fine-grained or object-level permissions.

Those belong in OpenFGA and Cerbos.

Summary

  • Zitadel answers who the user is
  • OpenFGA answers what the user is related to
  • Cerbos answers whether the action is allowed under current conditions
  • The API Gateway enforces the final decision

This results in a clean, scalable, and auditable authorization architecture.