Zitadel RBAC Overview - Liturgical-Calendar/LiturgicalCalendarAPI GitHub Wiki

The LiturgicalCalendar API uses Zitadel as its identity provider for OIDC-based authentication, role-based access control (RBAC), and user management, complemented by OpenFGA for fine-grained per-resource permissions (see OpenFGA Fine-Grained Authorization). This replaces the earlier self-hosted JWT-only authentication (Phase 0) with an enterprise-grade identity platform.

Architecture

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚   PostgreSQL    β”‚
                    β”‚   Port: 5432    β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚             β”‚             β”‚
              β–Ό             β–Ό             β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  Zitadel   β”‚  β”‚  Login V2 β”‚  β”‚  litcal  β”‚
     β”‚  database  β”‚  β”‚  (shares  β”‚  β”‚ database β”‚
     β”‚  (managed) β”‚  β”‚  Zitadel  β”‚  β”‚  (RBAC)  β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  network) β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
              β”‚             β”‚
              β–Ό             β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  Zitadel   β”‚  β”‚  Login V2 β”‚
     β”‚  Port:8080 β”‚  β”‚  Port:8081β”‚
     β”‚  OIDC/API  β”‚  β”‚  Auth UI  β”‚
     β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
            β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ LiturgicalCalendarβ”‚
  β”‚       API         β”‚
  β”‚   Port: 8000      β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Components

Service Port Purpose
Zitadel 8080 OIDC/OAuth2 identity provider, Console, Management API
Login V2 8081 Next.js authentication UI with passkeys and registration
PostgreSQL 5432 Shared database (Zitadel-managed + application RBAC)
API 8000 LiturgicalCalendar REST API

Middleware Pipeline

Protected routes pass through the following middleware chain:

  1. ErrorHandlingMiddleware - Catches exceptions and formats error responses
  2. LoggingMiddleware - Logs requests and responses
  3. HttpsEnforcementMiddleware - Requires HTTPS in production (auth/admin routes)
  4. OidcAvailabilityMiddleware - Returns 503 if Zitadel is not configured
  5. OidcAuthMiddleware - Validates OIDC tokens, extracts user info and roles
  6. AuthorizationMiddleware - Checks required Zitadel roles
  7. OpenFgaAuthorizationMiddleware - Fine-grained per-resource permission checks via OpenFGA (see OpenFGA Fine-Grained Authorization)

Role-based authorization (Zitadel) and resource-level authorization (OpenFGA) are two distinct layers: the role grants access to a class of endpoints, while OpenFGA determines which specific calendars or tests the user may act on. The rationale behind this design is described in the Calendar Governance Model.

Roles

Four roles are defined in the Zitadel project:

Role Purpose
admin System administrator; bypasses all permission checks
developer Register applications and generate API keys
calendar_editor Contribute calendar data (with per-calendar permissions)
test_editor Create and modify test definitions

Protected Routes

Authentication Endpoints (OIDC required)

Method Route Purpose
POST /auth/access-requests Submit an access request (role + permissions)
GET /auth/access-requests View own access requests
GET /auth/access-requests/status Check access request status
GET /auth/admin-scopes Caller's admin status and resource scopes
GET /auth/test-scopes Caller's test editor/admin scopes
POST /auth/email-verification/resend Resend verification email

Admin Endpoints (OIDC + admin role)

Method Route Purpose
GET /admin/access-requests List access requests
POST /admin/access-requests/{id}/approve Approve request (grants role + permissions)
POST /admin/access-requests/{id}/reject Reject access request
POST /admin/access-requests/{id}/revoke Revoke granted access (role + permissions)
GET /admin/permissions List OpenFGA permission tuples
POST /admin/permissions Grant a permission
DELETE /admin/permissions Revoke a permission
GET /admin/permissions/check Check a specific permission
GET /admin/notifications Pending item counts
GET /admin/users List users with roles
DELETE /admin/users/{userId}/roles/{role} Revoke a user's role
GET /admin/applications List all applications
POST /admin/applications/{uuid}/approve Approve application
POST /admin/applications/{uuid}/reject Reject application
POST /admin/applications/{uuid}/revoke Revoke approved application

Developer Endpoints (OIDC + developer role)

Method Route Purpose
GET /applications List own applications
POST /applications Register new application
GET /applications/{uuid} Application details
PATCH /applications/{uuid} Update application
DELETE /applications/{uuid} Delete application
POST /applications/{uuid}/resubmit Resubmit rejected app
GET /applications/{uuid}/keys List API keys
POST /applications/{uuid}/keys Generate API key
DELETE /applications/{uuid}/keys/{keyId} Revoke API key
POST /applications/{uuid}/keys/{keyId}/rotate Rotate API key

Calendar Data Endpoints (OIDC + calendar_editor + per-calendar permission)

Method Route Purpose
PUT /data/{category}/{calendar} Create calendar definition
PATCH /data/{category}/{calendar} Update calendar definition
DELETE /data/{category}/{calendar} Delete calendar definition

Test Data Endpoints (OIDC + test_editor role)

Method Route Purpose
PUT /tests Create test definition
PATCH /tests Update test definition
DELETE /tests Delete test definition

Database Schema

The application database (litcal) stores RBAC data that complements Zitadel's identity management. Fine-grained per-calendar permissions themselves live in OpenFGA as relationship tuples (see OpenFGA Fine-Grained Authorization); the application database stores the request workflow and the tuple outbox:

Table Purpose
access_requests Unified role + permission request workflow
openfga_outbox Transactional outbox for OpenFGA tuple writes/deletes
applications Registered developer applications
api_keys API keys with rate limiting, scope, and expiration
user_notification_state Per-user notification read state
audit_log Security and compliance audit trail

Tables use UUID primary keys via PostgreSQL's pgcrypto extension.

Key Source Files

File Purpose
src/Services/ZitadelService.php Zitadel Management API client
src/Http/Middleware/OidcAuthMiddleware.php OIDC token validation and role extraction
src/Http/Middleware/OidcAvailabilityMiddleware.php Checks if Zitadel is configured
src/Http/Middleware/AuthorizationMiddleware.php Zitadel role enforcement
src/Http/Middleware/OpenFgaAuthorizationMiddleware.php Per-resource permission checks (OpenFGA)
src/Services/OpenFgaClient.php OpenFGA API client
src/Http/Middleware/ApiKeyMiddleware.php API key extraction and validation
src/Repositories/ Database repositories for RBAC tables
src/Services/RateLimiter.php IP-based rate limiting (login endpoint)

Implementation Status

See the Implementation Status page for details on what is complete and what remains to be done.


Authentication & RBAC: [Home]] ](/Liturgical-Calendar/LiturgicalCalendarAPI/wiki/[[Calendar-Governance-Model) β†’