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:
- User clicks login
- Angular redirects to Zitadel
- User chooses Google or LINE
- Zitadel completes authentication
- 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:
- Validate access token
- Check relationship permission via OpenFGA
- Evaluate business policy via Cerbos
- 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.