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;oidcApplicationType—webnebonative; typ se volí před prvním provisionem a u existující aplikace se při synchronizaci zachová podle 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; - přesný
postLogoutRedirectUri, napříkladhttps://manager.amotion.cloud/; - jeden
brandId.
Nativní aplikace mohou místo HTTP(S) použít vlastní URI schéma, například com.atrea.manager:/oauth/callback. Hostname se u nich odvodí pouze pro zobrazení; brand resolver používá vždy přesnou Redirect URI.
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:
- u webové aplikace hostname bez protokolu a cesty, ale včetně portu, pokud ho callback používá (u nativní aplikace se hostname nezadává);
- přesný OIDC callback;
- přesnou Post Logout URI;
- jeden z podporovaných brandů.
Příklad:
| Aplikace | OIDC client | Hostname | Redirect URI | Post Logout URI | Brand |
|---|---|---|---|---|---|
| aManager | 383916237421346819 | manager.amotion.cloud | https://manager.amotion.cloud/callback | https://manager.amotion.cloud/ | Airflow |
| aManager | stejný | manager.vallox.cloud | https://manager.vallox.cloud/callback | https://manager.vallox.cloud/ | 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 Web nebo Native 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 Web/Native aplikace v tomto projektu;
- její Redirect URI a Post Logout URI se sjednotí s lokálními instancemi;
- 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.
Totéž platí pro Post Logout URIs: hodnota se už neodvozuje z originu callbacku, ale nastavuje se explicitně u každé instance. Při aktualizaci existujícího nativního klienta synchronizace znovu pošle OIDC_APP_TYPE_NATIVE, takže ho nepřepne na Web.
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.sqla nakonecsql/migrations/2026-09-02_native_application_instances.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.