Auth & Organizace — Specifikace¶
Přehled¶
Systém podporuje více firem (multi-tenant). Každá firma je identifikována IČO. Uvnitř firmy má každý uživatel přiřazené role a seznam viditelných aplikací. Oprávnění přiděluje firemní admin (ředitel) nebo super admin (Avaxis tým).
Hierarchie uživatelů¶
Super Admin (Avaxis)
└── vidí a spravuje vše — firmy, uživatele, katalog, support
│
▼ aktivuje aplikace pro firmu (na základě předplatného)
Company Admin (Ředitel / firemní admin)
└── spravuje uživatele své firmy
└── přiděluje aplikace rolím nebo konkrétním uživatelům
│
▼
Uživatel (zaměstnanec)
└── vidí pouze aplikace, které mu byly přiděleny
└── nemůže měnit oprávnění
Datový model¶
-- Firmy
companies (
id UUID PRIMARY KEY,
ico VARCHAR(8) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
address TEXT,
plan VARCHAR(50), -- basic | standard | premium
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ
)
-- Uživatelé
users (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255),
system_role VARCHAR(50), -- super_admin | company_admin | user
is_active BOOLEAN DEFAULT true,
is_deleted BOOLEAN DEFAULT false, -- soft delete (GDPR anonymizace)
deleted_at TIMESTAMPTZ,
tfa_method VARCHAR(20), -- NULL | 'totp' | 'qr_app' — aktivní metoda
tfa_enabled BOOLEAN DEFAULT false,
tfa_secret TEXT, -- šifrovaný TOTP secret / QR session klíč
tfa_disabled_by UUID REFERENCES users(id), -- kdo vypnul (support)
tfa_disabled_at TIMESTAMPTZ,
last_login_at TIMESTAMPTZ,
created_at TIMESTAMPTZ
)
-- Pojmenované role v rámci firmy (Účetní, Skladník, Obchodník...)
company_roles (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
name VARCHAR(100),
created_by UUID REFERENCES users(id)
)
-- Přiřazení role uživateli
user_company_roles (
user_id UUID REFERENCES users(id),
role_id UUID REFERENCES company_roles(id),
PRIMARY KEY (user_id, role_id)
)
-- Přidělení aplikací — per role NEBO per uživatel (ne obojí)
app_assignments (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
app_id UUID REFERENCES apps(id),
role_id UUID REFERENCES company_roles(id), -- NULL = per-user
user_id UUID REFERENCES users(id), -- NULL = per-role
granted_by UUID REFERENCES users(id),
granted_at TIMESTAMPTZ,
CONSTRAINT role_or_user CHECK (
(role_id IS NOT NULL AND user_id IS NULL) OR
(role_id IS NULL AND user_id IS NOT NULL)
)
)
Logika viditelnosti aplikací¶
Uživatel X z Firmy A vidí aplikaci Y pokud platí ALESPOŇ JEDNA podmínka:
1. app_assignments: company_id=A, app_id=Y, user_id=X
2. app_assignments: company_id=A, app_id=Y, role_id IN (role uživatele X)
Navíc musí platit:
• Firma A má aktivní předplatné pro aplikaci Y
• Aplikace Y je public NEBO custom pro company_id=A
SELECT DISTINCT a.*
FROM apps a
JOIN app_assignments aa ON aa.app_id = a.id
LEFT JOIN user_company_roles ucr
ON ucr.role_id = aa.role_id AND ucr.user_id = :user_id
WHERE aa.company_id = :company_id
AND (aa.user_id = :user_id OR ucr.user_id = :user_id)
AND (a.visibility = 'public' OR a.client_id = :company_id)
Autentizace — přihlášení¶
Základní přihlášení (e-mail + heslo)¶
POST /auth/login
{ email, password }
Pokud uživatel nemá 2FA:
→ vrátí access_token, refresh_token, offline_jwt
Pokud uživatel má 2FA aktivní:
→ vrátí { requires_2fa: true, session_token: "..." }
→ klient zobrazí výzvu pro 2FA
→ POST /auth/2fa/verify { session_token, code }
→ vrátí access_token, refresh_token, offline_jwt
JWT — access token payload¶
{
"sub": "usr_abc123",
"company_id": "comp_xyz",
"system_role": "user",
"tfa_verified": true,
"exp": 1745000000,
"iat": 1744999100
}
JWT — offline token payload (rozšířený)¶
{
"sub": "usr_abc123",
"company_id": "comp_xyz",
"system_role": "user",
"apps": ["fakturace-pro", "ucetnictvi-lite"],
"exp": 1747591100,
"iat": 1744999100,
"sig": "RS256 podpis soukromým klíčem Avaxis"
}
Refresh flow¶
Access token expiruje (15 min)
→ POST /auth/refresh { refresh_token }
→ nový access_token + rotovaný refresh_token
→ starý refresh_token okamžitě invalidován
Dvoufaktorová autentizace (2FA)¶
Principy¶
- Výchozí stav: 2FA je vypnuté pro všechny uživatele
- Aktivace: dobrovolná v Nastavení účtu po přihlášení
- Vypnutí podporou: support admin může vypnout 2FA (ztráta telefonu)
- Ověření před aktivací: uživatel musí prokázat funkčnost před zapnutím
Metody (obě dostupné, uživatel si vybere)¶
Metoda A — QR kód přes AVAX mobilní aplikaci
1. Přihlašovací obrazovka zobrazí QR kód (platnost 2 min)
2. Uživatel otevře AVAX Android appku
3. Naskenuje QR kód
4. Appka odešle podepsaný token na API
5. API ověří → přihlášení dokončeno
Metoda B — TOTP (Google Authenticator, Authy, 1Password...)
1. Přihlašovací obrazovka zobrazí pole pro 6místný kód
2. Uživatel otevře autentifikační appku
3. Zadá aktuální 6místný kód (platnost 30s)
4. API ověří TOTP secret → přihlášení dokončeno
Aktivace 2FA (flow)¶
Uživatel jde do Nastavení → Zabezpečení → Aktivovat 2FA
Krok 1 — Volba metody:
○ QR kód (AVAX mobilní aplikace)
○ TOTP kód (Google Authenticator / jiná appka)
Krok 2 — Nastavení:
TOTP: zobrazí QR kód pro naskenování do autentifikátoru
+ záložní kód (backup code) pro případ ztráty telefonu
QR app: odkaz ke stažení AVAX Android appky + párování
Krok 3 — OVĚŘENÍ FUNKČNOSTI (povinné před aktivací):
"Zadejte kód z vaší aplikace pro ověření správného nastavení"
[ 6místný kód nebo QR scan ]
→ pokud ověření úspěšné → 2FA aktivní
→ pokud neúspěšné → 2FA se NEAKTIVUJE, uživatel zkusí znovu
Krok 4 — Potvrzení aktivace:
"2FA je nyní aktivní. Uložte si záložní kódy na bezpečné místo."
[ Zobrazit záložní kódy ] [ Stáhnout PDF ]
Záložní kódy (backup codes)¶
Při aktivaci vygeneruje API 8 jednorázových záložních kódů (každý 16 znaků)
Uloží se jako bcrypt hash (nikdy plaintext)
Při použití záložního kódu → kód je okamžitě invalidován
Počet zbývajících kódů zobrazen v Nastavení
Pokud zbývá ≤ 2 kódy → upozornění uživateli
Vypnutí 2FA support adminem (ztráta telefonu)¶
Support admin v Správě portálu → detail uživatele → [Vypnout 2FA]
→ Povinná poznámka důvodu (zobrazí se v audit logu)
→ Záznam: { actor: support_admin, action: 'disable_2fa',
target_user: ..., reason: "...", timestamp: ... }
→ Uživateli přijde e-mail: "2FA bylo deaktivováno administrátorem.
Pokud jste toto nežádali, kontaktujte okamžitě support."
→ Uživatel se může znovu přihlásit bez 2FA a nově nastavit
Kdo může vypnout 2FA:
✓ Avaxis support_agent nebo super_admin
✗ Company admin nemůže vypnout 2FA svých uživatelů
(bezpečnostní pravidlo — jen neutrální třetí strana)
2FA a offline režim¶
Desktop aplikace s aktivním 2FA:
→ Online přihlášení: plné 2FA ověření
→ Offline spuštění (platný offline JWT): 2FA se nevyžaduje
(zařízení je již považováno za důvěryhodné po online přihlášení)
→ Offline JWT expiruje: nutné online přihlášení včetně 2FA
Databáza 2FA relací (QR metoda)¶
tfa_qr_sessions (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
session_token VARCHAR(64), -- náhodný token v QR kódu
status VARCHAR(20), -- 'pending' | 'approved' | 'expired'
device_id VARCHAR(255), -- mobilní zařízení které schválilo
expires_at TIMESTAMPTZ, -- +2 minuty od vytvoření
created_at TIMESTAMPTZ
)
tfa_backup_codes (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
code_hash VARCHAR(255), -- bcrypt hash
used_at TIMESTAMPTZ -- NULL = nevyužit
)
SSO¶
Rozhodnutí: Nyní implementujeme pouze e-mail + heslo. Architektura je připravena pro přidání OIDC v budoucnu — auth service je izolovaná vrstva, OIDC provider lze zapojit bez změny zbytku systému.
Správa uživatelů — Company Admin¶
Co může¶
- Vytvářet / deaktivovat uživatele své firmy
- Vytvářet pojmenované role (Účetní, Skladník...)
- Přiřazovat role uživatelům
- Přidělovat aplikace rolím nebo jednotlivým uživatelům
- Odebírat oprávnění
- Vidět přehled aktivit uživatelů (přihlášení, stažené aplikace)
- Vidět záznamy o impersonaci svých uživatelů Avaxis adminem
Co NEMŮŽE¶
- Přidělovat aplikace bez aktivního předplatného firmy
- Vidět chat nebo tikety jiných firem
- Spravovat uživatele jiných firem
- Vypnout 2FA svým uživatelům (pouze Avaxis support)
Audit log¶
Typy záznamů a retention¶
Typ záznamu Retention Úložiště po expiraci
────────────────────────────────────────────────────────────────────────────
Přihlášení / odhlášení 90 dní S3 archiv
Neúspěšné přihlášení (brute-force det.) 90 dní S3 archiv
Aktivace / deaktivace 2FA 1 rok S3 archiv
Vypnutí 2FA support adminem 1 rok S3 archiv (+ notif.)
Změny oprávnění (grant/revoke app) 1 rok S3 archiv
Vytvoření / deaktivace uživatele 1 rok S3 archiv
Impersonace (start + end) 1 rok S3 archiv
Změny předplatného / fakturace 7 let S3 archiv (daň. povin.)
GDPR souhlasy a žádosti o výmaz vztah + 3 r S3 archiv
Support komunikace vztah + 1 r S3 archiv
Schéma¶
audit_log (
id UUID PRIMARY KEY,
actor_id UUID REFERENCES users(id),
action VARCHAR(100),
-- přihlášení: 'login', 'logout', 'login_failed', 'login_2fa'
-- 2FA: 'tfa_enabled', 'tfa_disabled', 'tfa_disabled_by_support'
-- oprávnění: 'grant_app', 'revoke_app', 'create_role', 'assign_role'
-- uživatelé: 'create_user', 'deactivate_user', 'delete_user'
-- impersonace: 'impersonate_start', 'impersonate_end'
target_type VARCHAR(50), -- 'user' | 'role' | 'company' | 'app'
target_id UUID,
payload JSONB, -- before/after stav, IP adresa, device_id
retention VARCHAR(20), -- '90d' | '1y' | '7y' | 'contract+1y' ...
created_at TIMESTAMPTZ
)
Záznamy starší než jejich retention jsou automaticky přesunuty do S3 archivu
(levné úložiště, stále dostupné pro compliance audit, ale ne v hlavní DB).
Rozhodnuté otázky¶
| Otázka | Rozhodnutí |
|---|---|
| SSO | Nyní ne — pouze e-mail + heslo; architektura připravena pro OIDC |
| 2FA výchozí stav | Vypnuté, uživatel aktivuje dobrovolně v Nastavení |
| 2FA metody | QR kód (AVAX mobilní app) + TOTP záloha — obojí dostupné |
| 2FA aktivace | Povinné ověření funkčnosti před aktivací |
| 2FA vypnutí | Pouze Avaxis support_agent nebo super_admin (ne company admin) |
| 2FA vypnutí — notifikace | E-mail uživateli okamžitě po vypnutí |
| 2FA + offline | Offline JWT = důvěryhodné zařízení, 2FA se nevyžaduje |
| Audit log — přihlášení | 90 dní, pak S3 archiv |
| Audit log — oprávnění, 2FA | 1 rok, pak S3 archiv |
| Audit log — fakturace | 7 let (daňová povinnost) |
| Audit log — GDPR | Trvání vztahu + 3 roky |
| Audit log — archivace | Automatické přesunutí do S3 po uplynutí retention |
Otevřené otázky¶
Všechny otázky k auth-organization.md jsou vyřešeny.
Poslední aktualizace: 2026-04-21