Endpoint-uri de autentificare
Endpoint-uri de autentificare
API-ul de autentificare expune cele cinci endpoint-uri sub /api/v1/auth care alimentează fluxul JWT de înregistrare / autentificare / reîmprospătare / deconectare folosit de Portalul e-bon și de aplicația mobilă E-BON. Sunt singurele endpoint-uri din API pe care un apelant nou-venit le poate folosi fără să dețină deja credențiale — register, login, forgot-password și refresh sunt publice; logout cere un JWT valid din Portal pentru ca serverul să știe al cui jeton de reîmprospătare să-l revoce.
/api/v1/auth/*.Toată suprafața /api/v1/auth este protejată cu o limită de rată mai strictă decât restul API-ului. Vezi Durata jetoanelor și limite de rată mai jos pentru cifrele exacte. Plicul de eroare, regulile de idempotență și convențiile de paginare sunt documentate o singură dată pe Prezentarea API-ului; pe această pagină listăm doar codurile de eroare specifice fiecărui endpoint.
POST /api/v1/auth/register
Creează o organizație nou-nouță și utilizatorul Owner care o deține: provizionează un utilizator Firebase Auth, scrie documentele organizations/{orgId} și organizations/{orgId}/users/{uid}, opțional populează o primă locație din adresa ANAF furnizată, setează custom claims Firebase (orgId, role) pentru regulile de securitate Firestore și returnează o pereche proaspătă de jetoane de acces + reîmprospătare.
Autentificare: Public — nu necesită autentificare.
Corpul cererii
| Câmp | Tip | Obligatoriu | Note |
|---|---|---|---|
email | string | da | Adresă de email validă. Devine credențialul de autentificare Firebase Auth. |
password | string | da | Minimum 8 caractere. |
companyName | string | da | 1–255 caractere. Folosit atât ca nume al organizației, cât și ca displayName în Firebase Auth. |
cui | string | da | Cod fiscal românesc, max 20 caractere. Trebuie să respecte ^(RO)?\d{2,10}$ (ex. RO12345678 sau 12345678). |
address | string | nu | Până la 500 caractere. Când este furnizată, serverul o parsează prin parseAnafAddress și populează atât billingAddress, cât și o primă locație. |
Răspuns 201
{
"accessToken": "eyJhbGciOi...",
"refreshToken": "eyJhbGciOi...",
"userId": "user_xyz",
"orgId": "acme_corp"
}
Noul utilizator este creat cu rolul owner. Folosește accessToken imediat ca Authorization: Bearer <jwt> la apelurile ulterioare; persistă refreshToken în siguranță ca să poți apela POST /api/v1/auth/refresh odată ce jetonul de acces expiră.
Erori
VALIDATION_ERROR(400) — corpul nu a trecut validarea Zod (câmp lipsă, parolă mai scurtă de 8 caractere,cuimalformat, câmp prea lung).CONFLICT(409) —An account with this email already exists— Firebase Auth a refuzat cuauth/email-already-exists.RATE_LIMIT_EXCEEDED(429) — fereastra limitei de rată pentru autentificare a fost atinsă. Reîncearcă după secundele indicate deRetry-After.INTERNAL_ERROR(500) —Failed to create user account(eroare Firebase Auth neașteptată) sauFailed to create organization(scrierea în lot Firestore a eșuat; utilizatorul Firebase Auth creat parțial este derulat înapoi automat).
Catalogul HTTP complet este pe Prezentare API › Catalogul codurilor de eroare HTTP iar pașii de recuperare per cod se află pe referința de erori.
Exemplu
curl -X POST https://api.e-bon.ro/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "owner@acme.example",
"password": "o-parola-puternica",
"companyName": "Acme Corp SRL",
"cui": "RO12345678",
"address": "Str. Lipscani 12, sector 3, București"
}'
POST /api/v1/auth/login
Autentifică prin email + parolă față de Firebase Auth și returnează o pereche proaspătă de jetoane de acces + reîmprospătare. Handler-ul rezolvă orgId și role din custom claims-urile Firebase ale utilizatorului; dacă acelea lipsesc, cade înapoi pe o interogare collectionGroup('users') și scrie leneș claims-urile rezolvate înapoi, astfel încât autentificarea următoare să fie mai rapidă.
Autentificare: Public — nu necesită autentificare.
Corpul cererii
| Câmp | Tip | Obligatoriu | Note |
|---|---|---|---|
email | string | da | Adresă de email validă. |
password | string | da | Nu poate fi gol. |
Răspuns 200
{
"accessToken": "eyJhbGciOi...",
"refreshToken": "eyJhbGciOi...",
"userId": "user_xyz",
"orgId": "acme_corp"
}
Jetonul de reîmprospătare este stocat pe server ca hash SHA-256 sub organizations/{orgId}/users/{uid}/refreshTokens ca să poată fi ulterior revocat de /auth/logout sau rotit de /auth/refresh.
Erori
VALIDATION_ERROR(400) — corpul nu a trecut validarea Zod.UNAUTHORIZED(401) —Invalid email or password(returnat uniform pentru email necunoscut, parolă greșită sau un utilizator Firebase Auth fără apartenență Firestore — răspunsul nu dezvăluie niciodată care caz se aplică).RATE_LIMIT_EXCEEDED(429) — fereastra limitei de rată pentru autentificare a fost atinsă.INTERNAL_ERROR(500) —Authentication service misconfiguredcând serverului îi lipseșteFIREBASE_WEB_API_KEYși nu este îndreptat către emulatorul Firebase Auth.
Exemplu
curl -X POST https://api.e-bon.ro/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "owner@acme.example",
"password": "o-parola-puternica"
}'
POST /api/v1/auth/forgot-password
Cere serviciului Firebase Identity Toolkit să trimită emailul de resetare a parolei pentru adresa dată. Handler-ul returnează întotdeauna 200 — chiar și când emailul nu există sau apelul Firebase din amonte eșuează — astfel încât apelanții să nu poată enumera conturile sondând acest endpoint.
Autentificare: Public — nu necesită autentificare.
Corpul cererii
| Câmp | Tip | Obligatoriu | Note |
|---|---|---|---|
email | string | da | Adresă de email validă. |
Răspuns 200
{
"message": "If an account with that email exists, a password reset link has been sent."
}
Același corp de răspuns este returnat indiferent dacă emailul a corespuns sau nu unui utilizator Firebase Auth real; eșecurile din amonte sunt înregistrate pe server, dar suprimate din răspuns.
Erori
VALIDATION_ERROR(400) — corpul nu a trecut validarea Zod (câmpemaillipsă sau malformat).RATE_LIMIT_EXCEEDED(429) — fereastra limitei de rată pentru autentificare a fost atinsă.
FIREBASE_WEB_API_KEY, cererea este ignorată în tăcere — clientul primește în continuare plicul 200 de mai sus, dar niciun email nu este expediat. Operatorii ar trebui să verifice jurnalele API pentru FIREBASE_WEB_API_KEY not configured — forgot-password request ignored dacă emailurile de resetare nu ajung niciodată.Exemplu
curl -X POST https://api.e-bon.ro/api/v1/auth/forgot-password \
-H "Content-Type: application/json" \
-d '{ "email": "owner@acme.example" }'
POST /api/v1/auth/refresh
Schimbă un jeton de reîmprospătare încă valid pe o pereche nouă de jetoane de acces + reîmprospătare. Handler-ul verifică semnătura JWT, verifică dacă hash-ul SHA-256 al jetonului de reîmprospătare este încă prezent sub organizations/{orgId}/users/{uid}/refreshTokens (adică jetonul nu a fost revocat), apoi rotește jetonul de reîmprospătare: documentul Firestore anterior este șters și un hash nou este stocat. Jetonul de reîmprospătare vechi nu mai poate fi reutilizat după o reîmprospătare reușită.
Autentificare: Public — nu necesită autentificare (jetonul de reîmprospătare din corpul cererii este credențialul).
Corpul cererii
| Câmp | Tip | Obligatoriu | Note |
|---|---|---|---|
refreshToken | string | da | Nu poate fi gol. Jetonul de reîmprospătare de la /login, /register sau de la o reîmprospătare anterioară. |
Răspuns 200
{
"accessToken": "eyJhbGciOi...",
"refreshToken": "eyJhbGciOi..."
}
De reținut că — spre deosebire de /login și /register — acest răspuns nu include userId sau orgId. Noul jeton de reîmprospătare îl înlocuiește pe cel pe care l-ai trimis; persistă-l și aruncă-l pe cel vechi.
Erori
VALIDATION_ERROR(400) — corpul nu a trecut validarea Zod (câmprefreshTokenlipsă sau gol).UNAUTHORIZED(401) —Invalid or expired refresh token(verificarea semnăturii, a emitentului sau a TTL-ului a eșuat) sauRefresh token has been revoked(hash-ul nu mai este în Firestore — de obicei pentru că/logoutl-a șters sau pentru că o reîmprospătare anterioară l-a rotit deja).RATE_LIMIT_EXCEEDED(429) — fereastra limitei de rată pentru autentificare a fost atinsă.
Exemplu
curl -X POST https://api.e-bon.ro/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "eyJhbGciOi..." }'
POST /api/v1/auth/logout
Revocă jetonul de reîmprospătare furnizat ștergând hash-ul SHA-256 corespunzător din organizations/{orgId}/users/{uid}/refreshTokens. Jetonul de acces emis în prezent nu este invalidat — rămâne valid până la expirarea TTL-ului său scurt; bazarea pe deconectare pentru o tăiere imediată a accesului necesită așteptarea acelei ferestre.
Autentificare: JWT din Portal, orice utilizator autentificat.
Corpul cererii
| Câmp | Tip | Obligatoriu | Note |
|---|---|---|---|
refreshToken | string | da | Nu poate fi gol. Jetonul de reîmprospătare de revocat. |
Răspuns 200
{ "message": "Logged out successfully" }
Handler-ul returnează 200 chiar și când jetonul de reîmprospătare furnizat nu mai este în Firestore — deconectarea este idempotentă, deci o deconectare duplicată nu generează eroare. Trimiterea unui jeton de reîmprospătare care aparține altui utilizator este permisă de rută, dar inofensivă: doar documentele de sub calea {orgId}/{userId} a apelantului sunt scanate.
Erori
VALIDATION_ERROR(400) — corpul nu a trecut validarea Zod.UNAUTHORIZED(401) — JWT din Portal lipsă sau invalid.RATE_LIMIT_EXCEEDED(429) — fereastra limitei de rată pentru autentificare a fost atinsă.
Exemplu
curl -X POST https://api.e-bon.ro/api/v1/auth/logout \
-H "Authorization: Bearer <portal-jwt>" \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "eyJhbGciOi..." }'
Durata jetoanelor și limite de rată
| Jeton | TTL implicit | Configurat prin |
|---|---|---|
| Jeton de acces | 15 min | Variabila de mediu JWT_ACCESS_EXPIRES_IN. |
| Jeton de reîmprospătare | 7 zile | Variabila de mediu JWT_REFRESH_EXPIRES_IN. |
Ambele jetoane sunt JWT-uri semnate (iss: e-bon-api, sub: <userId>). Jetonul de acces folosește JWT_SECRET; jetonul de reîmprospătare folosește un JWT_REFRESH_SECRET separat. Jetoanele de reîmprospătare sunt în plus stocate pe server ca hash-uri SHA-256 ca să poată fi revocate de /logout și rotite de /refresh.
| Limitator | Max implicit | Fereastră | Cheie | Se aplică la |
|---|---|---|---|---|
/api/v1/auth/* | 30 | 10 min | IP client | Toate cele cinci rute de mai sus. |
Limita pentru autentificare este configurată de AUTH_RATE_LIMIT_MAX și AUTH_RATE_LIMIT_WINDOW_MS. Aceleași antete Retry-After și RateLimit-* documentate la Prezentare API › Limite de rată se aplică.
Fluxul de resetare a parolei
Endpoint-ul /auth/forgot-password nu resetează el însuși parola — doar declanșează emailul. Tot dus-întorsul este:
Clientul apelează POST /api/v1/auth/forgot-password cu emailul utilizatorului
API-ul redirecționează cererea către Firebase Identity Toolkit cu requestType: PASSWORD_RESET. Firebase decide dacă adresa aparține unui cont real; API-ul răspunde întotdeauna 200 cu același mesaj generic indiferent.
Utilizatorul primește emailul și apasă pe link-ul de resetare
Link-ul indică către pagina de resetare găzduită de Firebase (sau către un URL personalizat actionCodeSettings când este configurat în consola Firebase). API-ul e-bon nu este implicat în acest pas.
Firebase verifică codul de acțiune și acceptă o parolă nouă
Odată ce parola nouă este salvată, jetoanele de reîmprospătare ale utilizatorului stocate în Firestore nu sunt revocate automat de Firebase. Pentru a forța deconectarea fiecărei sesiuni existente, utilizatorul (sau un Owner) ar trebui să apeleze explicit POST /api/v1/auth/logout pentru fiecare jeton de reîmprospătare cunoscut sau să aștepte expirarea TTL-ului de 7 zile al jetonului de reîmprospătare.
Utilizatorul se autentifică din nou cu POST /api/v1/auth/login
Noua autentificare emite o pereche proaspătă de jetoane de acces + reîmprospătare și stochează hash-ul jetonului de reîmprospătare pe calea Firestore standard.
Vezi și
- Autentificare — prezentare conceptuală, formatul cheii API și cele nouă permisiuni.
- API Utilizatori — odată autentificat,
GET /api/v1/users/mereturnează profilul autentificat șiPOST /api/v1/users/me/change-passwordpermite unui utilizator conectat să-și rotească propria parolă. - API Organizații și Locații — rute cu JWT din Portal pentru profilul organizației, adresa de facturare, locații și abonații la notificări.
- Prezentare API — URL de bază, plic de eroare, limite de rată, idempotență, paginare, catalog complet de coduri de eroare HTTP.
- Referință erori — pași de recuperare per cod pentru fiecare cod de eroare HTTP returnat de API.
API Utilizatori
Endpoint-uri REST pentru profilul propriu al utilizatorului autentificat — citire, editare nume afișat și număr de telefon, schimbare parolă.
Endpoint-uri de stare, identitate și meta
Referință pentru sondele publice de stare, endpoint-ul autentificat de introspecție a identității, fișierul robots și suprafața OpenAPI (Swagger UI + spec brut) expuse de API-ul e-bon.