Skip to content

JWT Provideři ​

Konfigurace ​

JWT provideři se konfigurují v config/default.yaml (nebo production.yaml) pod klíčem token.jwt:

yaml
token:
  keyRefreshIntervalMinutes: 60   # Interval refreshe JWKS klíčů
  jwt:
    - provider: zitadel
      baseUrl: http://zitadel:8080
      jwk: /oauth/v2/keys
      issuer: http://localhost:8080
      hostOverride: localhost       # Volitelné: přepíše host při fetch klíčů

Jediným providerem Jabcore Platform je Zitadel. API přijme jen token účtu, který patří do některého user poolu — pool se určuje podle organizace, která uživatele vlastní. Token jiného providera by žádný pool neměl, a API ho proto odmítne (401).

Google / Microsoft přihlášení

Google a Microsoft nejsou samostatní JWT provideři. Federovaný login přes tyto účty se konfiguruje uvnitř Zitadelu jako externí Identity Provider (IdP). Token, který nakonec dorazí na API, vždy podepisuje Zitadel.

Zitadel (primární provider) ​

Zitadel je self-hosted OIDC provider, který zajišťuje autentizaci pro všechny aplikace Jabcore.

Speciální claims ​

Zitadel přidává do JWT tokenu navíc dynamické claims, kde je část identifikátoru zakódovaná přímo v názvu klíče:

Vzor claimuPopis
urn:zitadel:iam:org:id:{orgId}Organizace kontextu přihlášení — z názvu klíče se extrahuje {orgId}
urn:zitadel:iam:user:resourceowner:idOrganizace, která vlastní uživatele = jeho user pool. Pokud ho klient nevyžádá (scope urn:zitadel:iam:user:resourceowner), API organizaci dohledá v Zitadelu podle sub a výsledek cachuje.
urn:zitadel:iam:org:project:{projectId}:rolesRole uživatele v daném Zitadel projektu

Extrakce se provádí regexem v JWTService — orgId i projectId nejsou hodnoty, ale součást názvu claimu.

Organizace a user pool ​

Každý účet patří do organizace svého user poolu. Autorizační middleware z tokenu odvodí pool (req.token.userPoolId, userPoolCode, isPlatformPool) a účet dál identifikuje dvojicí pool + e-mail. Token je odmítnut, když:

  • organizace uživatele nepatří žádnému poolu,
  • client_id tokenu patří aplikaci z jiného poolu, než je pool účtu.

Login flow (PKCE) ​

Dev-only endpoint

Endpointy /api/zitadel/login a /api/zitadel/callback jsou určené pro lokální vývoj a testování. Callback vrací tokeny jako JSON odpověď (nikoliv redirect na frontend). V produkci provádí OIDC flow přímo frontend aplikace přes Zitadel.

1. GET /api/zitadel/login?client_id=<clientId>
   → Redirect na Zitadel login stránku (s PKCE)

2. Uživatel se přihlásí v Zitadel

3. GET /api/zitadel/callback?code=<authCode>&state=<state>
   → Výměna auth code za tokeny
   → JSON response: { access_token, id_token, id_token_claims, ... }

Webhoky ​

Zitadel posílá webhooky při událostech uživatele:

UdálostAkce v API
user.createdVytvoří uživatele v DB (pokud neexistuje)
user.deactivatedDeaktivuje uživatele (active = false)
user.reactivatedAktivuje uživatele (active = true)
user.removedDeaktivuje uživatele (active = false) — záznam se zachovává pro audit trail

Webhook endpoint: POST /api/zitadel/webhook
Autentizace: X-Webhook-Secret header

Tělo webhooku ​

json
{
  "eventType": "user.created",
  "orgId": "org_abc123",
  "user": {
    "email": "novy.uzivatel@firma.cz",
    "firstName": "Jan",
    "lastName": "Novák"
  }
}

Google / Microsoft (federovaní přes Zitadel) ​

Login přes Google nebo Microsoft není konfigurován v Jabcore Platform API. Tito poskytovatelé jsou nastaveni jako externí Identity Provideři uvnitř Zitadelu (Console → Default Settings → Identity Providers). Při přihlášení:

  1. Uživatel klikne v Zitadel login UI na "Sign in with Google/Microsoft"
  2. Zitadel provede OAuth flow proti Google/Microsoftu
  3. Zitadel vytvoří/aktualizuje lokálního uživatele a vydá vlastní JWT
  4. API přijme Zitadel JWT a verifikuje ho proti Zitadel JWKS

Z pohledu API tedy existuje vždy jediný provider — Zitadel. Účet vytvořený přes Google/Microsoft vzniká v organizaci user poolu aplikace, přes kterou se uživatel poprvé přihlásil; v jiném poolu je to jiný účet. upn claim zůstává v emailové extrakci pro případy, kdy Zitadel propaguje původní Microsoft UPN do tokenu.

Jak probíhá verifikace ​

typescript
// JWTService — zjednodušeně
async verifyToken(token: string): Promise<DecodedToken> {
  for (const provider of this.providers) {
    try {
      const decoded = jwt.verify(token, provider.keys, {
        algorithms: ['RS256', 'RS384', 'RS512'],
        issuer: provider.issuer,
      });
      return { ...decoded, provider: provider.name };
    } catch {
      // Zkusí další provider
    }
  }
  throw new ApiException(401, 'Invalid token');
}

Tokenem projdou všichni provideři v pořadí konfigurace. První úspěšná verifikace vyhrává.

Troubleshooting ​

Token is invalid ​

  • Ověřte, že issuer v konfiguraci odpovídá iss claimu v tokenu
  • Ověřte, že JWKS endpoint je dostupný ze serveru
  • Zkontrolujte expiraci tokenu (exp claim)

JWKS fetch failed ​

  • Zkontrolujte dostupnost baseUrl + jwk endpointu
  • Pro Zitadel v Dockeru: ujistěte se, že zitadel hostname je resolvovatelný
  • Použijte hostOverride pokud se liší interní a externí hostname

Email extraction failed ​

  • Zkontrolujte, které claims váš provider vydává
  • Přidejte logiku extrakce do JWTService.extractEmail() pokud používáte nestandartní claim

Jabcore Platform — interní dokumentace