11. Roadmap autentizace, autorizace a API perimetru¶
Pracovní roadmap k 3. 9. 2026. Tento dokument navazuje na 10. Stav autentizace a autorizace vlastního backendu. Slouží jako předávací dokument pro další běh Codexu nebo vývojáře: popisuje cílový stav, proč se změna dělá, a kontrolní seznam kroků, které se budou postupně škrtat.
Zatím nejde o implementační diff. Před úpravami kódu vždy nejdřív ověř aktuální stav repozitáře, protože v pracovním stromu mohou být rozdělané změny.
Cíl¶
Aplikace má mít bezpečně oddělené tři provozní zóny:
- Interní personální část pro administrátory, recepci, terapeuty a finance.
Všichni uživatelé z tabulky
users, včetně terapeutů, se budou přihlašovat pomocí passkey autentizace. - Klientský portál pro klienty. Klienti budou mít vlastní session nad tabulkou
clients, ne session nad tabulkouusers. Přihlášení klienta bude e-mailem. SMS se použije při registraci/onboardingu k ověření platnosti telefonního čísla, ne jako druhý faktor každého přihlášení. - Veřejné části dostupné bez přihlášení: veřejné nabídky, start klientského loginu, onboarding/platba/storno přes jednorázové tokeny a další landing stránky.
Základní pravidlo: klientská identita se nikdy nesmí dostat do interního JWT/session
světa personálu. Personální /api nesmí přijmout klientskou cookie a veřejné
/api/public nesmí omylem otevřít interní data.
Proč se to dělá¶
Současný backend má jeden typ přihlášené identity: users.id. AuthMiddleware ověří
platnou cookie, ale obecně nekontroluje roli. Některé routy řeší oprávnění uvnitř
controllerů, některé ne.
Kdyby klientský portál začal vydávat stejnou session jako personál, klient by mohl
dostat technicky platnou session pro interní /api. To je špatný bezpečnostní model,
i kdyby se část rizika následně lepila kontrolami v jednotlivých controllerech.
Současně má aplikace části, které mají být veřejné, a části, které mají být dostupné jen z intranetu/VPN. To nejde čistě vyřešit, dokud jsou všechny backend endpointy namíchané pod jedním perimetrem.
Cílové rozdělení rout¶
Potvrzené rozhodnutí k 3. 9. 2026: používáme prefixy /api,
/api/public, /api/auth a /api/public/client_auth. Pokud se později zvolí
dvě domény nebo dva buildy, prefixy mohou zůstat stejné; změní se jen proxy/deploy
pravidla.
/api¶
Interní API pro personál.
- Dostupnost: intranet/VPN/reverzní proxy podle nasazení.
- Autentizace: personální passkey session.
- Subjekt:
user_id. - Middleware: personální middleware + explicitní role/oprávnění.
- Nikdy nepřijímá klientskou session.
/api/public¶
Veřejné a portálové API pro klienty a terapeuty.
- Dostupnost: internet.
- Subjekt podle endpointu:
- žádný subjekt pro veřejné čtení,
client_idpro klientsky autentifikované endpointy,user_idpro terapeutsky autentifikované portálové endpointy, protože terapeuti jsou účty v tabulceusers,- jednorázový token pro onboarding, platbu, storno a podobné odkazy.
- Nikdy nevrací interní personální data.
- Nikdy nepovoluje přístup k libovolnému klientovi podle
client_idz requestu. - Nikdy nepovoluje přístup k libovolnému terapeutovi podle ID z requestu bez ověření passkey session a role/oprávnění.
/api/auth¶
Personální autentizace.
- Passkey register/login challenge a callback.
- Logout a refresh personální session.
- Starý password/magic-link login postupně odstranit nebo ponechat jen jako výslovně povolený recovery/admin režim.
/api/public/client_auth¶
Klientská autentizace.
- Start loginu e-mailem.
- Vydání klientské session po ověření e-mailového kódu/linku.
- Refresh/logout klientské session.
- Onboarding token validation a onboarding completion.
- SMS ověření telefonu při registraci/onboardingu.
Fáze 1: mapa endpointů¶
Stav k 3. 9. 2026: základní perimetr je připravený bez plošného přepisování
frontendu. Interní /api zůstává personální surface za staff autentizací. První cílový
veřejný klientský vstup je /api/public/client_auth. Dočasná kompatibilní cesta
/api/functions/clientAuth zůstává zachovaná, dokud frontend nepřejde na explicitní
/api/public volání. Pro Base44-like klientské volání existuje veřejný alias
/api/public/functions/clientAuth. Stejný /api/public prefix bude používat i
terapeutský portál; liší se až konkrétní autentizační/session middleware a subjekt
endpointu.
| Endpoint | Typ | Stav |
|---|---|---|
/api/* |
staff-auth/intranet | interní API, nadále chráněné personálním middleware |
/api/auth/* |
staff-auth | cílově passkey callback/refresh/logout pro personál |
/api/public/client_auth |
public/client-auth/token-only | připravená veřejná route pro klientskou autentizaci |
/api/public/functions/clientAuth |
public/client-auth kompatibilita | veřejný Base44-like alias pro klientské clientAuth akce |
/api/public/therapist_auth |
public/staff-passkey | cílová veřejná route pro passkey login terapeutů z tabulky users |
/api/functions/clientAuth |
public kompatibilita | dočasný alias kvůli existujícímu frontendu |
/api/functions/{name} |
staff-auth kompatibilita | dočasná Base44 kompatibilní vrstva pro interní funkce |
/api/clients, /api/orders, /api/invoices, /api/paymentLogs |
staff-auth | zatím interní, role se řeší ve Fázi 6 |
Označení typů:
public: bez přihlášení, ale bez interních dat,client-auth: vyžaduje budoucí klientskou session,staff-passkey: vyžaduje passkey autentizaci účtu z tabulkyusers, i když je endpoint dostupný přes veřejný/api/publicportálový povrch,staff-auth: vyžaduje personální session,token-only: vyžaduje jednorázový token, typicky onboarding/platba/storno.
Cílové session modely¶
Potvrzené rozhodnutí k 3. 9. 2026: cílové cookies budou explicitně rozlišovat
personál a klienty. Dnešní obecné auth_token, auth_refresh_token a auth_session
jsou jen přechodný stav pro existující personální JWT autentizaci.
Potvrzené rozhodnutí k 3. 9. 2026: cílově budou session tabulky oddělené:
staff_sessions pro personál a client_sessions pro klienty. Dnešní sessions
tabulka je migrační zdroj pro existující personální session, ne cílový sdílený model.
Personál¶
Tabulka: users.
Cookies:
staff_auth_tokenstaff_auth_refresh_tokenstaff_auth_session
Cookie paths:
- access token:
/api - refresh token:
/api/auth - session marker:
/
JWT claims:
sub: ID zuserssub_type:staffsession_idiatexp
Session tabulka:
staff_sessions
Klient¶
Tabulka: clients.
Cookies:
client_auth_tokenclient_auth_refresh_tokenclient_auth_session
Cookie paths:
- access token:
/api/public - refresh token:
/api/public/client_auth - session marker:
/
JWT claims:
sub: ID zclientssub_type:clientsession_idiatexp
Session tabulka:
client_sessions
Platnost:
- access token: 30 minut
- refresh token: 7 dní
- absolutní platnost session: 30 dní
Potvrzené rozhodnutí k 3. 9. 2026: klientská session použije stejné základní časové limity jako dnešní personální JWT model: 30 minut access token, 7 dní refresh token a 30 dní absolutní session. Důvod je jednodušší migrace a předvídatelné chování napříč aplikací. Citlivé změny jako změna e-mailu, telefonu nebo bezpečnostních údajů musí i tak vyžadovat nové MFA ověření a mohou zneplatnit existující klientské sessions.
Middleware:
ClientAuthMiddlewarenastavuje jenclient_idaclient_session_id.- Personální controllery nikdy nečtou
client_id. - Klientské controllery nikdy nečtou
user_idpro rozhodování o přístupu.
Klientský login a ověření telefonu¶
Potvrzené rozhodnutí k 3. 9. 2026: klienti se nebudou běžně přihlašovat
dvoufaktorově. Přihlášení klienta bude e-mailem, podobně jako dnešní existující
clientAuth flow. SMS se použije při registraci/onboardingu pro ověření, že klient
zadává platné telefonní číslo.
Cílový login klienta má být e-mailový challenge-based flow:
- Klient zadá e-mail.
- Server vytvoří login challenge a odešle 6místné jednorázové heslo e-mailem.
- Klient zadá jednorázové heslo.
- Server vydá klientskou session.
Důležité vlastnosti:
- E-mailové jednorázové heslo je 6místné číslo, ne dlouhý link/hash v e-mailu.
- Jednorázové heslo má krátkou expiraci, typicky 5-10 minut.
- Jednorázové heslo je v DB uložené jen jako HMAC hash, ne jako plaintext.
- Hash jednorázového hesla je unikátní, protože podle samotného zadaného kódu se dohledává login challenge a klient.
- Pokusy jsou limitované.
- Odpovědi nesmí jednoduše prozrazovat, jestli e-mail existuje.
- Samotný výběr klienta podle
client_idnesmí nahrazovat ověření e-mailové challenge.
Potvrzené rozhodnutí k 3. 9. 2026: SMS provider a základní integrační vzor vezmeme
z projektu /home/uhlir/práca/mediasolution/cpmpk.
Referenční implementace:
- SMS wrapper:
/home/uhlir/práca/mediasolution/cpmpk/core/SmsManager/SmsManager.php - generátor kódů:
/home/uhlir/práca/mediasolution/cpmpk/core/Codegenerator/SmsCodeGenerator.php - legacy odeslání:
/home/uhlir/práca/mediasolution/cpmpk/app/tags/programs-cards/00-sms/send-sms.php - legacy ověření:
/home/uhlir/práca/mediasolution/cpmpk/app/tags/programs-cards/00-sms/verify-sms-code.php - legacy migrace logu:
/home/uhlir/práca/mediasolution/cpmpk/app/migrations/2025-06-27.sql
Provider:
- endpoint:
https://api.smsmngr.com/v2/message - konfigurace:
SMSMANAGER_API_KEY - payload:
{"to":[{"phone_number":"+420..."}],"body":"..."} - úspěch: přítomnost
accepted[0].message_id - chyba nízkého kreditu v referenci:
rejected[0].code === 78
Pro NEO se nepřenáší legacy tag architektura ani plaintext uložení kódu. Přenést se má jen provider wrapper a ergonomie kódu, ale upravené pro Slim:
SmsServicevbackend/src/Services,- konfigurace přes
.env, bez zápisu do.env, - normalizace telefonu na mezinárodní tvar,
- bezpečné timeouty a zpracování curl chyb,
- jednotný návrat
{success, provider_message_id, error_code, raw_response}, - logování bez ukládání plaintext OTP do aplikačních logů.
SMS OTP pro NEO klientskou registraci/onboarding musí zůstat součástí challenge modelu:
- kód ukládat hashovaně,
- navázat kód na konkrétní klientskou registrační/onboarding challenge,
- omezit opakované odeslání, minimálně cooldown 5 minut podle vzoru cpmpk,
- ověřovací platnost držet 5-10 minut,
- po úspěšném ověření označit challenge faktor jako splněný.
Onboarding je zvláštní případ:
- E-mailový onboarding link může být jednorázový token.
- Pokud klient ještě nemá ověřený telefon, telefon doplní v onboardingu.
- Před uložením souhlasů a dokončením profilu se ověří SMS OTP na nově zadaný telefon.
- Po dokončení se onboarding token označí jako použitý.
Passkey pro uživatele z users¶
Passkey zavést jako primární autentizaci všech účtů v tabulce users: administrátorů,
recepce, terapeutů, financí a dalších interních rolí.
Potvrzené rozhodnutí k 3. 9. 2026: nepíšeme lokální WebAuthn implementaci přímo
do NEO backendu. Použije se existující centrální auth server
/home/uhlir/práca/mediasolution/neo-auth-passkey, který už používá Petrovo CMS v
/home/uhlir/práca/mediasolution/cms_git.
Referenční implementace:
- server:
/home/uhlir/práca/mediasolution/neo-auth-passkey - CMS klient:
/home/uhlir/práca/mediasolution/cms_git/app/Integrations/Auth/AuthClient.php - CMS dokumentace:
/home/uhlir/práca/mediasolution/cms_git/docs/app/admin/auth-passkey-cms.md - WebAuthn knihovna na serveru:
web-auth/webauthn-lib^5.3
NEO aplikace má dělat totéž co CMS:
- přesměrovat uživatele z tabulky
usersna centrální auth server, - přijmout callback s JWT tokenem,
- ověřit RS256 JWT přes veřejný klíč auth serveru,
- spárovat identitu podle e-mailu na lokální
users.email, - založit vlastní lokální staff session,
- držet autorizaci a role lokálně v NEO aplikaci.
Auth server dál řeší:
- ukládání WebAuthn credentialů,
- registraci passkey přes pozvánku,
- login challenge,
- validaci signature, origin, RP ID, challenge, credential ID a sign counter,
- vlastní rate limiting a expiraci challenge.
NEO bude potřebovat úpravy konfigurace a drobné úpravy centrálního auth serveru podle
cílové domény/redirectů, ale základ je už existující auth-passkey projekt.
Recovery:
Potvrzené rozhodnutí k 3. 9. 2026: zatím nebudeme dělat samoobslužný recovery flow pro personál. Praktický model je:
- každý účet v tabulce
usersmá mít ideálně aspoň dvě passkeys, - při ztrátě přístupu owner/admin ověří člověka mimo aplikaci,
- owner/admin pošle novou passkey pozvánku přes centrální auth server,
- staré ztracené passkeys se podle potřeby zneplatní na auth serveru,
- událost musí být auditovaná.
Poznámka: magic link není MFA. Nahrazuje heslo přístupem do e-mailu. Pro účty z tabulky
users se nemá používat jako běžný fallback, protože by snižoval bezpečnost passkey
modelu.
Autorizace interního API¶
Interní /api musí mít výchozí stav zakázáno.
Potřebujeme zavést jeden z modelů:
- role middleware na route skupinách,
- permission middleware nad schopnostmi,
- kombinace role middleware + jemnějších kontrol v controlleru.
Minimální role/oprávnění k rozhodnutí:
- owner/company_admin,
- recepce nebo admin klientské části,
- terapeut,
- finance,
- globální admin/superadmin, pokud existuje.
První endpointy k uzamčení:
/api/clients/api/orders/api/{invoices|paymentLogs}/api/apps/{appId}/entities/{Invoice|PaymentLog}/api/clientCategories/api/notifications/api/users/api/therapists
Veřejný/intranet perimetr¶
Cílově musí být reverzní proxy schopná rozlišit:
- veřejné cesty: frontend klientského portálu, landing stránky,
/api/public, - intranetové cesty: administrační frontend a
/api.
Možné varianty:
- Dvě domény a dva buildy.
- Jeden build, ale proxy dělení podle cesty.
Pragmatická varianta pro tento projekt je zatím druhá možnost: jeden build, ale jasné cesty a proxy pravidla. Dlouhodobě mohou být dvě domény čistší.
Stav k 3. 9. 2026¶
- Existuje personální JWT cookie autentizace nad
users. - Existuje
AuthSessionServices cookieauth_token,auth_refresh_token,auth_session. AuthMiddlewareověřuje session, ale obecně nekontroluje roli.docs/10-backend-autorizace.mdpopisuje rizika současného stavu.- V pracovním stromu mohou být rozdělané změny kolem
sendNotification,clientAuth, onboarding tokenů a DB schématu. Před další prací vždy zkontrolujgit statusa aktuální diff.
Roadmap checklist¶
Fáze 0: Stabilizace rozhodnutí¶
- Potvrdit názvy route prefixů:
/api,/api/public, případně/api/auth. - Potvrdit názvy cookies pro personál a klienty.
- Rozhodnout, jestli personální session zůstane v
sessions, nebo vzniknestaff_sessions. - Vybrat WebAuthn/passkey PHP knihovnu.
- Vybrat SMS provider nebo potvrdit existující provider v projektu.
- Rozhodnout, jak dlouho platí klientská session a refresh token.
- Rozhodnout, jak bude fungovat recovery pro personál bez passkey.
Fáze 1: API perimetr bez změny chování¶
- Sepsat cílovou mapu endpointů: interní
/apivs veřejné/api/public. - Připravit nové route skupiny bez přesunu logiky.
- Označit endpointy podle typu: public, client-auth, staff-auth, token-only.
- Přidat integrační testy, že
/apibez staff session vrací 401. - Přidat integrační testy, že
/api/publicnevyžaduje staff session tam, kde nemá. - Zachovat dočasný veřejný alias
/api/functions/clientAuth, aby současný frontend dál fungoval při postupném přechodu na/api/public/client_auth.
Fáze 2: Oddělená klientská session¶
- Přidat tabulku
client_sessions. - Přidat
ClientSessionService. - Přidat
ClientAuthMiddleware. - Přidat klientské cookies s oddělenými názvy a path ideálně
/api/public. - Přidat refresh/logout pro klienta.
- Upravit frontend
ClientAuthContext, aby nepoužíval lokální důvěru vlocalStoragejako autentizaci. - Ověřit, že klientská cookie nefunguje na interním
/api. - Ověřit, že personální cookie nefunguje jako klientská identita.
Fáze 3: Klientský e-mail login a SMS ověření telefonu¶
- Navrhnout tabulky pro login challenges a OTP pokusy.
- Ukládat e-mailová jednorázová hesla hashovaně.
- Implementovat e-mailový login challenge pro klienta.
- Přidat samostatné registrační akce
request_registration_otpaverify_registration_otpvedle login-onlyrequest_otp/verify_otp. - Implementovat SMS OTP provider pro ověření telefonu při registraci/onboardingu.
- Přidat rate limiting podle IP a kontaktu.
- Sjednotit chybové odpovědi proti enumeraci klientů.
- Vydat klientskou session po úspěšném ověření e-mailového jednorázového hesla.
- Přidat testy pro ověření přes hashovaný 6místný kód a vydání klientské session.
Fáze 4: Onboarding přes jednorázový token¶
Stav k 4. 9. 2026: onboarding pozvánky používají jednorázový token v URL.
Token se validuje na explicitní veřejné route /api/public/client_auth/onboarding.
Dokončení onboardingu na této route i na dočasné kompatibilní clientAuth
akci vyžaduje SMS ověřovací kód pro stejný client_id a telefon. Frontend už
neukládá právně významné souhlasy přes běžný Client.update.
- Vytvořit jednorázové onboarding tokeny při odeslání pozvánky.
- Posílat v e-mailu URL s tokenem, ne jen
client_id. - Validovat token na
/api/public/client_auth/onboarding. - Zneplatnit starší tokeny při opětovném odeslání pozvánky.
- Označit token jako použitý po dokončení onboardingu.
- Vyžadovat SMS ověření telefonu před uložením souhlasů.
- Přestat ukládat právně významné souhlasy přes běžný
Client.updatez frontendu.
Fáze 5: Passkey pro účty z tabulky users¶
Doplněné rozhodnutí k 9. 9. 2026: základní admin/recovery flow bude postavené na stejném mechanismu jako registrace passkey přes pozvánku. Administrace musí umět odeslat passkey registrační pozvánku při vytvoření uživatele a také později z detailu uživatele tlačítkem pro opětovné odeslání. Při ztrátě všech passkeys admin/owner nejdřív ověří identitu člověka mimo aplikaci, potom pošle novou pozvánku a podle potřeby zneplatní staré passkeys na centrálním auth serveru. Recovery akce musí být auditované.
Doplněné rozhodnutí k 9. 9. 2026: bootstrap úplně prvního administrátora a nouzové
recovery bez přihlášeného admina se řeší CLI commandem spuštěným na serveru, ne veřejnou
URL. Command pošle passkey pozvánku jen existujícímu aktivnímu
admin/owner/company_admin účtu a akci zapíše do auditu jako
auth.passkeyBootstrapInvite.
cd /var/www/backend
php bin/send-passkey-invite.php admin@example.com
php bin/send-passkey-invite.php admin@example.com --revoke-existing
Doplněné rozhodnutí k 9. 9. 2026: stav interního účtu řídí i passkey přístup. Při vytvoření aktivního uživatele se odešle passkey pozvánka, při vytvoření neaktivního uživatele ne. Při deaktivaci uživatele se zneplatní jeho passkeys na centrálním auth serveru. Při opětovné aktivaci se odešle nová registrační pozvánka. Při smazání uživatele se před odstraněním lokálního účtu zneplatní jeho passkeys.
- Použít centrální tabulku
passkeysvneo-auth-passkeymísto lokální tabulkyuser_passkeysv NEO. - Použít centrální tabulku
passkey_challengesvneo-auth-passkeypro WebAuthn challenges. - Implementovat registraci passkey přes centrální invite flow.
- Implementovat passkey login přes centrální auth server.
- Napojit passkey login na personální session přes
/api/auth/passkey/callback. - Přidat UI pro správu vlastních passkeys.
- Přidat správu passkey pozvánek v administraci: odeslání při vytvoření uživatele a opětovné odeslání z detailu uživatele.
- Použít správu passkey pozvánek jako základní admin/recovery flow pro uživatele, kteří ztratili všechny passkeys; součástí musí být audit a možnost zneplatnit staré passkeys na centrálním auth serveru.
- Vypnout nebo omezit starý password/magic-link login.
- Odstranit user enumeration z magic-link endpointu, pokud zůstane.
- Přihlášení na passkey serveru má vypadat stejně jako portál, tedy použít podobné CSS.
- Přidat CLI command pro bootstrap prvního administrátora a nouzové recovery bez přihlášeného admina.
- Navázat passkey pozvánky a zneplatnění passkeys na aktivaci, deaktivaci a smazání interního uživatele.
Fáze 6: Autorizace interních rout¶
- Rozšířit
AuthMiddlewarenebo přidat novýStaffAuthMiddleware. - Zavést explicitní požadavky na role/oprávnění na route skupinách.
- Uzamknout
/api/clients. - Uzamknout
/api/orders. - Uzamknout faktury a payment logy.
- Uzamknout nastavení, uživatele a terapeuty.
- Přidat testy pro běžného terapeuta, recepci, finance a admina.
Fáze 7: Proxy a nasazení¶
- Připravit pravidla pro veřejné cesty.
- Připravit pravidla pro intranetové cesty.
- Zkontrolovat cookie
Secure,HttpOnly,SameSiteaPath. - Nastavit produkční
APP_URLna veřejnou doménu, nelocalhost. - Odstranit nebo zamknout
/api/test/tokens. - Zdokumentovat finální routy pro infrastrukturu.
Fáze 8: Úklid starého Base44 kompatibilního povrchu¶
- Zmapovat všechna
base44.functions.invoke(...)ve frontendu. - Nahradit klientské volání explicitními
/api/publicendpointy. - Nahradit interní volání explicitními
/apiendpointy. - Po přepnutí frontendu odstranit veřejný alias
/api/functions/clientAuth. - Zrušit nebo výrazně omezit obecný
/api/functions/{name}dispatcher. - Odstranit tvrdě zadané Base44 app ID z rout, pokud už nebude potřeba.
Definice hotovo¶
Cíl je splněný, až platí:
- klient se umí přihlásit přes e-mail a dostane jen klientskou session,
- registrace/onboarding klienta ověří telefon přes SMS,
- klientská session neotevře žádné interní
/api, - všichni uživatelé z tabulky
users, včetně terapeutů, se umí přihlásit passkey autentizací, - interní routy mají explicitní role/oprávnění,
- veřejné routy jsou pod
/api/public, - onboarding a podobné odkazy používají jednorázové tokeny,
- proxy/infrastruktura umí oddělit veřejný portál od intranetové administrace,
- testy pokrývají oddělení staff/client identit a nejrizikovější autorizační cesty.