openova/platform/keycloak/README.md
hatiyildiz 70fea3ab8f docs(pass-34): banned-term TENANT sweep + keycloak hostname drift
GLOSSARY's banned term "tenant" survived in Configuration tables and Flux
postBuild substitutions across product READMEs as ${TENANT} (uppercase
ENV var). Prior banned-term greps searched lowercase `tenant` so the
ALL-CAPS form slipped through.

Product README fixes:
- products/cortex: TENANT/DOMAIN → ORGANIZATION/SOVEREIGN_DOMAIN, plus
  two DNS placeholder fixes for llm-gateway and chat URLs (same shape
  Pass 25/31 fixed elsewhere).
- products/fingate: 6 instances (Flux substitution, Configuration table,
  4 URL templates) renamed. URL shape api.openbanking.<org>.<sov-dom>
  flagged as 4-segment FQDN that doesn't match NAMING §5.1 or §5.2 —
  deferred to a deeper architectural pass.
- products/fabric: Configuration table row renamed.

Component README:
- platform/keycloak: shared-sovereign hostname auth.<sovereign-domain>
  and per-organization auth.<org>.<sovereign-domain> both missing
  <location-code> per NAMING §5.1. Fixed.

platform/librechat ${TENANT_ID} preserved — that's Microsoft Azure AD
tenant-ID (external technology, exempted by GLOSSARY).

Validation log Pass 34 entry includes meta-note: always run a global
grep for the surfaced drift category before closing a pass, to avoid
the asymmetric-drift problem Pass 25 warned against.
2026-04-27 22:42:50 +02:00

228 lines
5.8 KiB
Markdown

# Keycloak
User identity for Catalyst Sovereigns. Per-Sovereign supporting service in the Catalyst control plane (see [`docs/PLATFORM-TECH-STACK.md`](../../docs/PLATFORM-TECH-STACK.md) §2.3). Also serves as the FAPI Authorization Server for the Fingate (Open Banking) Blueprint.
**Status:** Accepted | **Updated:** 2026-04-27
> **Catalyst topology** (set at Sovereign provisioning time, see [`docs/SECURITY.md`](../../docs/SECURITY.md) §6):
> - **`per-organization`** (SME-style Sovereigns, e.g. omantel): one minimal Keycloak per Organization (single replica, embedded H2/sqlite, ~150 MB RAM, no HA). Blast radius limited to one Org.
> - **`shared-sovereign`** (corporate self-host, e.g. bankdhofar): one HA Keycloak for the entire Sovereign with multiple realms (one per Organization), federating to the corporation's identity provider (Azure AD, Okta).
---
## Overview
Keycloak provides:
- **User identity** for the Catalyst console, marketplace, admin, REST/GraphQL API, and per-Application SSO.
- **OIDC / OAuth 2.0 / SAML** federation to corporate IdPs.
- **FAPI 2.0** compliant authorization for the Fingate Open Banking Blueprint:
- PSD2/FAPI 2.0 certification path
- eIDAS certificate validation
- Consent management
- Multi-tenant TPP support (PSD2 sense — Third Party Providers, not platform tenants)
---
## Architecture
```mermaid
flowchart TB
subgraph Keycloak["Keycloak"]
Core[Core IAM]
FAPI[FAPI Module]
Consent[Consent Service]
end
subgraph Backend["Backend"]
CNPG[CNPG Postgres]
end
subgraph Integration["Integration"]
Envoy[Envoy/Cilium]
TPP[TPP Registry]
end
Envoy -->|"ext_authz"| FAPI
FAPI --> Consent
Core --> CNPG
FAPI --> TPP
```
---
## FAPI 2.0 Compliance
| Feature | Status |
|---------|--------|
| PKCE | Required |
| Signed JWT requests | Required |
| mTLS client auth | Required |
| PAR (Pushed Authorization) | Required |
| JARM responses | Required |
---
## Configuration
### Keycloak Deployment
The deployment shape depends on Catalyst's `keycloakTopology` choice (see banner above):
**Corporate (`shared-sovereign`)** — one HA Keycloak per Sovereign in `catalyst-keycloak` namespace on the management cluster:
```yaml
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: keycloak
namespace: catalyst-keycloak
spec:
instances: 3 # HA, multiple replicas
db:
vendor: postgres
host: keycloak-postgres-rw.catalyst-keycloak.svc
port: 5432
database: keycloak
usernameSecret: # ESO-managed via OpenBao
name: keycloak-db-credentials
key: username
passwordSecret:
name: keycloak-db-credentials
key: password
http:
tlsSecret: keycloak-tls
hostname:
hostname: auth.<location-code>.<sovereign-domain>
```
**SME (`per-organization`)** — one minimal Keycloak per Organization in the Org's namespace on the management cluster:
```yaml
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: keycloak
namespace: <org> # per-Org namespace
spec:
instances: 1 # no HA at SME tier
db:
vendor: postgres # or H2/sqlite for the smallest tier
host: keycloak-postgres-rw.<org>.svc
port: 5432
database: keycloak
# ... credentials
hostname:
hostname: auth.<org>.<location-code>.<sovereign-domain>
```
### FAPI Realm Configuration
```json
{
"realm": "open-banking",
"enabled": true,
"sslRequired": "all",
"attributes": {
"fapi.compliance.mode": "strict",
"pkce.required": "S256",
"require.pushed.authorization.requests": "true"
},
"clientPolicies": {
"policies": [
{
"name": "fapi-advanced",
"enabled": true,
"conditions": [
{
"condition": "client-roles",
"configuration": {
"roles": ["fapi-client"]
}
}
],
"profiles": ["fapi-2-security-profile"]
}
]
}
}
```
---
## eIDAS Certificate Validation
TPP certificates are validated against qualified trust service providers:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: eidas-config
namespace: open-banking
data:
trust-anchors: |
# QTSPs for eIDAS validation
- name: qualified-tsp-1
certificate: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
```
---
## TPP Client Registration
```json
{
"clientId": "tpp-12345",
"clientAuthenticatorType": "client-jwt",
"redirectUris": ["https://tpp.example.com/callback"],
"attributes": {
"tpp.authorization.number": "PSDGB-FCA-123456",
"tpp.eidas.certificate": "...",
"tpp.roles": ["AISP", "PISP"]
},
"defaultClientScopes": [
"openid",
"accounts",
"payments"
]
}
```
---
## Consent Flow
```mermaid
sequenceDiagram
participant TPP
participant Keycloak
participant User
participant ConsentService
TPP->>Keycloak: PAR request
Keycloak->>TPP: request_uri
TPP->>User: Redirect to Keycloak
User->>Keycloak: Authenticate
Keycloak->>ConsentService: Get consent page
ConsentService->>User: Show accounts/permissions
User->>Keycloak: Grant consent
Keycloak->>ConsentService: Store consent
Keycloak->>TPP: Authorization code
```
---
## High Availability
HA shape depends on Catalyst's `keycloakTopology`:
- **`shared-sovereign`** (corporate): 3 replicas behind a Service, CNPG PostgreSQL with WAL streaming to async standby, session replication via Infinispan.
- **`per-organization`** (SME): single replica, no session replication, restart-on-deploy is acceptable for SME-tier SLAs. Larger SMEs can opt into HA via tier upgrade — same Catalyst CR shape, just bumped `instances`.
---
*Part of [OpenOva](https://openova.io)*