Tmavý režim
Multi-brand přihlášení pro více instancí aplikace
Brand se už neposílá v callbacku ani v OIDC scope. Spravuje se centrálně v atrea-user-api a Login v2 ho dohledá podle OIDC klienta a přesného callbacku.
Datový model
Brand
Každý brand má zitadelOrgId. Stejné ID se používá:
- při loginu pro načtení vzhledu a registračního kontextu brandu;
- při odesílání ZITADEL notifikací pro výběr brandu a jeho SMTP.
ZITADEL organizace tedy nadále používáme, ale jejich ID neposílá frontend.
Aplikace
Aplikace obsahuje:
oidcClientId— ID jejího OIDC klienta v ZITADELu;zitadelProjectIdazitadelApplicationId— vazbu na spravované vzdálené objekty;zitadelManaged— zda jejich životní cyklus vlastníatrea-user-api;- stav poslední synchronizace (
unconfigured,pending,synced,error); supportedBrandIds— seznam brandů, které aplikace smí používat;- libovolný počet instancí.
Seznam podporovaných brandů funguje stejně jako seznam podporovaných jazyků. Brand instance musí být v tomto seznamu.
Instance aplikace
Instance představuje jeden nasazený callback a obsahuje:
hostname, napříkladmanager.amotion.cloudnebolocalhost:8083;- přesný
redirectUri, napříkladhttps://manager.amotion.cloud/callback; - jeden
brandId.
Jeden host včetně portu nesmí být přiřazen různým brandům. Může mít více callback cest, pokud všechny používají stejný brand. Port je součást identity instance, takže například localhost:8081 a localhost:8083 mohou používat různé brandy.
Průběh loginu
text
Aplikace
│ client_id + obyčejný redirect_uri + openid profile email
▼
ZITADEL vytvoří auth request
▼
Login v2 načte clientId a redirectUri
▼
GET atrea-user-api/api/zitadel/login-brand
▼
instance → brand → zitadelOrgId → správný vzhled loginu
▼
globální vyhledání a ověření účtu → původní callback aplikaceVeřejný resolver vyžaduje přesnou dvojici clientId + redirectUri. Uživatel si proto nemůže zvolit jiný brand query parametrem.
Nastavení v admin panelu
1. Brand
V Brandy otevři brand a vyplň ZITADEL Organization ID. Bez něj brand nelze přiřadit instanci aplikace.
2. Aplikace
V Aplikace otevři nebo vytvoř aplikaci a nastav:
- Podporované brandy — vyber všechny brandy, které může aplikace používat;
- změny ulož.
U nové aplikace se Client ID nevyplňuje. Po založení první instance vytvoří user-api automaticky ZITADEL projekt i OIDC aplikaci a získané Client ID uloží. Client ID se ručně zadává jen při jednorázovém připojení existující aplikace.
3. Instance
V detailu aplikace v části Instance aplikace přidej pro každý deployment:
- hostname bez protokolu a cesty, ale včetně portu, pokud ho callback používá (volitelné koncové
/se při uložení odstraní); - přesný OIDC callback;
- jeden z podporovaných brandů.
Příklad:
| Aplikace | OIDC client | Hostname | Callback | Brand |
|---|---|---|---|---|
| aManager | 383916237421346819 | manager.amotion.cloud | https://manager.amotion.cloud/callback | Airflow |
| aManager | stejný | manager.vallox.cloud | https://manager.vallox.cloud/callback | Vallox |
Přidání další instance nebo brandu nevyžaduje novou verzi Login v2 ani aplikace. V managed režimu stačí změna v administraci; synchronizace callback automaticky promítne do ZITADELu.
Automatická správa ZITADELu
atrea-user-api je zdroj pravdy. Jedné lokální aplikaci odpovídá právě jeden managed ZITADEL projekt a v něm právě jedna OIDC SPA aplikace. Projekt není sdílený mezi více lokálními aplikacemi. OIDC redirect URI jsou přesně odvozené ze všech aktuálních application_instances dané aplikace.
Managed projekty vlastní centrální platformní organizace nastavená přes ZITADEL_PROJECT_ORG_ID. V ZITADELu mají vypnuté vyžadování project role a project grantu, aby brand organizace neomezovala přihlášení uživatele. Přístup do produktu zůstává v rolích a oprávněních atrea-user-api.
Synchronizace postupuje idempotentně:
- lokální požadovaný stav se uloží a označí jako
pending; - vytvoří se nebo ověří ZITADEL projekt;
- vytvoří se nebo ověří jedna OIDC SPA aplikace v tomto projektu;
- její callbacky se sjednotí s callbacky lokálních instancí;
- uloží se vzdálená ID a
oidcClientId, smaže se stará chyba a stav přejde nasynceds časem vzitadelSyncedAt.
Pokud vzdálený krok selže, lokální požadovaný stav zůstane zachovaný, stav je error a detail je v zitadelSyncError. Opakovaný sync pokračuje bezpečně ze stejných ID a nesmí vytvářet další projekty nebo OIDC aplikace. Login používá poslední úspěšně synchronizované nastavení; změna instance se proto považuje za hotovou až ve stavu synced.
Převzetí existující aplikace
Aktuální ZITADEL konfiguraci není nutné vytvářet znovu. Do lokální aplikace se importují zitadelProjectId, zitadelApplicationId a oidcClientId, ověří se, že OIDC aplikace opravdu patří do daného projektu, a provede se první porovnání callbacků. Import začíná s zitadelManaged = false, takže smazání lokálního záznamu nikdy nesmaže převzatý projekt. Po kontrole lze vlastnictví explicitně přepnout na managed režim.
Mazání
Vzdálené objekty se kaskádově mažou jen pro zitadelManaged = true. Protože má managed projekt jedinou OIDC aplikaci, stačí úspěšně smazat projekt a ZITADEL smaže jeho aplikaci. Teprve potom se smaže lokální aplikace. Když ZITADEL mazání selže, lokální záznam se zachová se stavem error, aby nevznikl osiřelý projekt. U importovaných (zitadelManaged = false) aplikací se maže pouze lokální vazba.
Callbacky v ZITADELu
V ZITADELu musí být jednou registrovaný každý skutečně používaný callback. Callback se už neduplikuje pro různá organization ID. V managed režimu tento seznam neupravuj ručně: synchronizace ho vždy vrátí do stavu definovaného lokálními instancemi.
Správně:
text
http://localhost:8080/callback
https://manager.amotion.cloud/callback
https://manager.kagb.cloud/callbackNepoužívat:
text
/callback?brand_org_id=377696186024394755
/callback?brand_org_id=377963078832095235Lokální HTTP callback vyžaduje u OIDC aplikace zapnutý Development Mode.
Konfigurace klientské aplikace
Frontend potřebuje už jen běžné OIDC hodnoty:
env
APP_ZITADEL_AUTHORITY=https://zitadel.auth.amotion.cloud/
APP_ZITADEL_CLIENT_ID=<OIDC_CLIENT_ID>
APP_ZITADEL_REDIRECT_URI=http://localhost:8080/callback
APP_ZITADEL_POST_LOGOUT_URI=http://localhost:8080APP_ZITADEL_ORG_ID se nepoužívá. Scopes zůstávají:
text
openid profile emailProč nepoužívat organization scope
Scope urn:zitadel:iam:org:id:<ORG_ID> není volba vzhledu. Omezuje přihlášení na organizační kontext a může zabránit dokončení OIDC flow uživatelem z jiné organizace. Brand proto řeší pouze centrální mapování instance.
Organizace účtu a brand cílové aplikace jsou nezávislé. Například účet Atrea se může přihlásit do Vallox instance a uvidí Vallox login. Přístup do aplikace dál řídí role, grants a oprávnění v atrea-user-api.
API
Admin CRUD:
GET /api/applications/:appCode/instancesPOST /api/applications/:appCode/instancesPUT /api/applications/:appCode/instances/:instanceIdDELETE /api/applications/:appCode/instances/:instanceIdGET /api/applications/:appCode/zitadel/statePOST /api/applications/:appCode/zitadel/syncPOST /api/applications/:appCode/zitadel/detach
Veřejný serverový resolver pro Login v2:
text
GET /api/zitadel/login-brand?clientId=<CLIENT_ID>&redirectUri=<URI>Endpoint vrací pouze identifikaci aplikace, instance a brandu; SMTP hesla ani jiná tajemství nezpřístupňuje.
Pořadí nasazení
- Na existující databázi spusť v tomto pořadí
sql/migrations/2026-08-03_application_instances.sqlasql/migrations/2026-08-03_zitadel_application_sync.sql, potom nasaďatrea-user-api. Pro novou databázi je stejné schéma už vsql/init.sql. - U brandů zkontroluj
zitadelOrgId. - Existující ZITADEL aplikace nejdřív importuj jako unmanaged; nové aplikace založ rovnou v managed režimu.
- U aplikace nastav podporované brandy a instance a spusť synchronizaci.
- Před pokračováním ověř stav
synceda výsledné obyčejné callbacky. - Nasaď Login v2 s nastaveným
USER_API_URL. - Nasaď klienty bez
APP_ZITADEL_ORG_IDa bez org scope.
Staré query callbacky odstraň ze ZITADELu až po ověření nového flow.
Pro aktuální aManager a KGB je připravený jednorázový bezpečný import sql/migrations/2026-08-03_adopt_existing_zitadel_apps.sql. Uloží známá project, application a client ID, ale ponechá zitadelManaged = false. V panelu se pak zobrazí současné vzdálené callbacky a diff proti lokálním instancím. Protože ze starých callbacků s více experimentálními brand_org_id nelze jednoznačně poznat výsledný brand domény, brand každé lokální instance se při převzetí vybere jednou ručně.
Diagnostika
- Špatný nebo výchozí brand: zkontroluj
oidcClientId, přesnou hodnoturedirectUri, přiřazený brand a jehozitadelOrgId. - Po loginu „Spravovat profil“: zkontroluj, že scope neobsahuje
urn:zitadel:iam:org:id:*a klient používá obyčejný callback. invalid_request: callback klienta se přesně neshoduje s callbackem registrovaným v ZITADELu.- Resolver vrací 404: dvojice
clientId + redirectUrinení v instancích aplikace nastavena. - Synchronizace je
error: zkontrolujzitadelSyncError, dostupnost ZITADELu a oprávnění service accountu; po opravě spusť sync znovu.
Unikátnost e-mailu
ZITADEL může mít stejné e-mailové údaje u účtů v různých organizacích. Pokud má v celé instanci existovat jen jeden účet na e-mail, musí to vynucovat centrální provisioning a atrea-user-api. Login uživatele vyhledává globálně, aby účet nebyl omezen brand organizací cílové aplikace.