AngularAtlased. 22.2

Cap. 17Securitate & globalizare74 / 75

OAuth 2.1 și OpenID Connect cu PKCE

Fluxul authorization code cu PKCE, pas cu pas: verifier și challenge, state și nonce, ID token-ul și de ce un BFF e mai sigur.

arhitectură ~0 min de citit

Când te loghezi cu Google, Microsoft, Keycloak sau Auth0, aplicația ta nu vede niciodată parola. Trimite utilizatorul la furnizorul de identitate, care îl trimite înapoi cu un cod de unică folosință; aplicația schimbă codul pe tokenuri. OAuth 2.1 — versiunea curățată a OAuth 2.0 — păstrează un singur flow pentru aplicațiile din browser: authorization code cu PKCE.

PKCE (Proof Key for Code Exchange, pronunțat „pixy”) rezolvă o problemă precisă. O aplicație single-page e un client public: nu are niciun secret pe care să-l ascundă, pentru că tot ce livrează poate fi citit. Are deci nevoie de altă cale să dovedească faptul că cine răscumpără codul e același cu cine l-a cerut. Demo-urile din pagină folosesc criptografie reală (Web Crypto), nu o imitație.

Dacă încă folosești implicit flow
Implicit flow (response_type=token, tokenuri în fragmentul URL-ului) a dispărut din OAuth 2.1: tokenurile din URL ajung în istoricul browserului, în loguri și în headerele Referer. Trecerea la code flow cu PKCE e de obicei o schimbare de configurare în bibliotecă și la furnizor.

Flow-ul, în șapte pași

Aplicația pregătește câteva valori aleatorii, trimite browserul la furnizor, primește înapoi un cod și îl schimbă — împreună cu o dovadă — pe tokenuri. Doar ultimul pas implică API-ul tău. Parcurge-l în demo; bifează „atacator” ca să vezi ce oprește verificarea lui state.

app (browser)                         furnizor de identitate                 API
  │ 1. verifier, challenge = S256(verifier), state, nonce                     │
  │ 2. GET /authorize?…&code_challenge&state&nonce ─▶│                         │
  │                                  3. login, MFA, consimțământ               │
  │◀─ 4. 302 /callback?code=…&state=…  (verifică state!)                       │
  │ 5. POST /token  code + code_verifier ────────────▶│ S256(verifier) == challenge?
  │◀─ 6. access_token, id_token, refresh_token ───────│                         │
  │ 7. GET /orders  Authorization: Bearer <access_token> ─────────────────────▶│
LiveAuthorization code cu PKCE, pas cu pas
1verifier, challenge, state, nonce
2GET /authorize
3login, MFA, consimțământ
4302 /callback?code&state
5POST /token + verifier
6access, ID, refresh token
7GET /api … Bearer
Apasă „Înainte” prin toți cei șapte pași. Apoi bifează „Callback falsificat” și ia-o de la capăt: flow-ul se oprește la pasul 4.

PKCE: dovedești că tu ai cerut

Înainte de redirect, aplicația generează un code_verifier aleator și trimite la /authorize doar hash-ul lui, code_challenge. Furnizorul îl ține minte lângă cod. Când aplicația răscumpără codul la /token, trimite chiar verifier-ul; furnizorul îi face hash și compară. Cine interceptează codul — o aplicație rău-intenționată înregistrată pe același redirect, un log, o extensie de browser — nu are verifier-ul, iar codul nu îi folosește la nimic.

/** base64url fără padding (RFC 4648 §5), cum cere PKCE */
function base64url(bytes: Uint8Array): string {
  let binary = '';
  for (const b of bytes) binary += String.fromCharCode(b);
  return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

/** 32 de octeți aleatori → 43 de caractere (minimul permis e 43, maximul 128) */
export function createVerifier(): string {
  return base64url(crypto.getRandomValues(new Uint8Array(32)));
}

/** code_challenge = BASE64URL(SHA-256(code_verifier)), metoda S256 */
export async function challengeFor(verifier: string): Promise<string> {
  const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
  return base64url(new Uint8Array(digest));
}

// RFC 7636, anexa B:
// challengeFor('dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk')
//   → 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'
LiveVerifier, challenge și verificarea de la /token
  1. 1Aplicația generează un verifier

    0 caractere · sunt permise între 43 și 128

    code_challenge…
  2. 2Doar challenge-ul pleacă la /authorize

    Furnizorul nu a primit încă nimic.

  3. 3Răscumpără codul la /token
Generează un verifier, trimite challenge-ul, apoi răscumpără codul în ambele feluri. Încarcă exemplul din RFC 7636 ca să verifici implementarea față de standard.

Folosește S256, niciodată plain (cu plain, challenge-ul este verifier-ul, deci cine vede redirect-ul are tot). Verifier-ul stă în sessionStorage — doar tabul ăsta — și e șters imediat ce codul a fost schimbat.

state, nonce și redirect_uri

  • state — o valoare aleatoare salvată înainte de redirect și verificată în /callback. Fără ea, un atacator poate trimite victima la callback-ul tău cu codul atacatorului, iar victima ajunge logată în contul atacatorului (login CSRF). Și PKCE blochează asta, dar furnizorii tot cer state și oricum ai nevoie de el ca să-ți porți propriul context.
  • nonce — o valoare aleatoare trimisă la /authorize care trebuie să se întoarcă în ID token. Dovedește că ID token-ul a fost emis pentru acest login, nu copiat dintr-unul anterior.
  • redirect_uri — înregistrat la furnizor exact, fără wildcard-uri. Iar unde mergi după login (returnTo) salvează aplicația ta înainte de redirect; nu-l lua niciodată dintr-un ?returnUrl= din bara de adrese, altfel pagina de login devine un open redirect.
import { challengeFor, createVerifier } from './pkce';

const AUTHORIZE = 'https://id.example.com/authorize';

export async function login(returnTo = '/'): Promise<void> {
  const verifier = createVerifier();
  const state = createVerifier(); // aleator, de unică folosință
  const nonce = createVerifier();

  // sessionStorage: doar tabul ăsta, dispare când se închide tabul
  sessionStorage.setItem('oauth', JSON.stringify({ verifier, state, nonce, returnTo }));

  const url = new URL(AUTHORIZE);
  url.search = new URLSearchParams({
    response_type: 'code',
    client_id: 'atlas-spa',
    redirect_uri: `${location.origin}/callback`, // înregistrat EXACT, fără wildcard-uri
    scope: 'openid profile email',
    code_challenge: await challengeFor(verifier),
    code_challenge_method: 'S256',
    state,
    nonce,
  }).toString();
  location.assign(url);
}

Trei tokenuri, trei roluri

Răspunsul de la /token conține până la trei tokenuri, iar confuzia dintre ele e cea mai frecventă greșeală. ID token-ul îi spune aplicației cine s-a logat. Access token-ul îi spune API-ului ce are voie apelantul să facă. Refresh token-ul obține access tokenuri noi fără un login nou. Să decodezi un JWT în browser e util pentru afișare, dar nu e validare: semnătura se verifică cu cheile publice ale furnizorului (JWKS), iar asta e treaba unei biblioteci.

ID tokenAccess tokenRefresh token
Citit deaplicația taAPI-ul tăufurnizor
Trimis lanicăieri — rămâne în aplicațiedoar originea API-ului tăudoar /token-ul furnizorului
Trăieșteminute5–60 de minuteore sau zile, rotit la fiecare folosire

Mai sigur: un BFF păstrează tokenurile

După pasul 6, tokenurile stau în browser — exact unde le poate atinge un XSS. Documentul IETF despre OAuth pentru aplicații din browser descrie o opțiune mai puternică: un Backend for Frontend. Un server mic pe propriul tău site face schimbul codului, ține tokenurile pe server și îi dă browserului doar un cookie de sesiune HttpOnly, pe care JavaScript nu îl poate citi.

browser (SPA)                BFF (serverul tău, același site)           furnizor de identitate
  │ GET /auth/login ──────────▶│ PKCE + state + nonce, păstrate pe server ──▶│ /authorize
  │◀─ 302 către furnizor ──────│                                             │
  │                    …login la furnizor, redirect înapoi la BFF…           │
  │                            │ code + verifier ──────────────────────────▶│ /token
  │                            │◀── access, id, refresh (rămân AICI) ────────│
  │◀─ Set-Cookie: session=…; HttpOnly; Secure; SameSite=Strict               │
  │ GET /api/orders (cookie) ─▶│ pune Authorization: Bearer … ──▶ API        │

SPA-ul apelează /api pe propria origine; BFF-ul adaugă access token-ul și se ocupă de refresh. Un XSS poate încă face cereri cât timp pagina e deschisă, dar nu poate fura tokenuri ca să le folosească în altă parte. Prețul: încă o componentă de rulat și protecție CSRF pentru cookie (SameSite, un header anti-CSRF). Pentru tokenurile ținute în browser, interceptorul cu coadă și refresh e pe pagina despre tokenuri și refresh.

În Angular: o bibliotecă, un guard și un interceptor

Nu implementa protocolul singur. Biblioteci certificate ca angular-auth-oidc-client sau angular-oauth2-oidc se ocupă de descoperirea furnizorului, PKCE, validarea ID token-ului inclusiv semnătura, refresh și logout. Codul din pagina asta arată ce fac ele, ca să le poți configura și depana cu ochii deschiși. Partea ta:

  • Configurarea: authority, client id, redirect_uri, scope-uri și code flow cu PKCE — de obicei implicit.
  • Un guard pe rutele private, care pornește login-ul și ține minte unde să revină. Vezi guards & resolvers.
  • Un interceptor care adaugă access token-ul doar pentru originea API-ului tău. Un token trimis la orice URL ajunge la terți; vezi securitatea pentru restul imaginii.

Când DA, când NU

OAuth / OIDC
  • Login printr-un furnizor de identitate extern: SSO de companie, Google, Microsoft, Apple.
  • Mai multe aplicații care împart același login și aceiași utilizatori.
  • MFA, passkeys și recuperarea contului, pe care preferi să le lași specialiștilor.
Ceva mai simplu
  • O singură aplicație cu propriii utilizatori și propriul backend: un cookie de sesiune clasic poate fi tot ce îți trebuie. Tokenurile și refresh-ul au pagina lor.
  • Apeluri server-to-server: acolo e flow-ul client credentials, pe server, niciodată în browser.
  • Să scrii singur protocolul în producție: folosește o bibliotecă certificată.

Capcane frecvente

  • Implicit flow sau tokenuri în URL. Ajung în istoric, în loguri și în headerul Referer.
  • Fără verificarea lui state. Callback-ul acceptă un cod pe care aplicația ta nu l-a cerut niciodată.
  • ID token-ul trimis la API. E pentru aplicație; API-ul primește access tokenuri, cu audience-ul corect.
  • Decodare în loc de validare. Fără verificarea semnăturii, oricine poate scrie un JWT cu orice claims.
  • Verifier-ul în localStorage, păstrat după folosire. Locul lui e în sessionStorage și moare odată cu schimbul.
  • ?returnUrl= luat din bara de adrese. Pagina ta de login devine un open redirect folosit la phishing.

Fișă rapidă

SPA-uri (OAuth 2.1)       authorization code + PKCE, nimic altceva (fără implicit flow)
code_verifier             43–128 caractere aleatorii, în sessionStorage, șters după folosire
code_challenge            BASE64URL(SHA-256(verifier)), metoda S256 (niciodată "plain")
state                     aleator, verificat pe /callback: oprește login CSRF
nonce                     aleator, trebuie să fie în ID token: oprește tokenurile reluate
redirect_uri              înregistrat exact, fără wildcard-uri; returnTo salvat ÎNAINTE de redirect
ID token                  cine s-a logat (pentru aplicație); validezi iss, aud, exp, nonce, semnătura
access token              ce ai voie să apelezi (pentru API); doar către originea API-ului tău
refresh token             rotit la fiecare folosire sau deloc în browser
cel mai sigur             un BFF: tokenurile pe server, un cookie HttpOnly în browser
logout                    sesiunea locală + end_session_endpoint al furnizorului
nu-l scrie singur         biblioteci certificate: angular-auth-oidc-client, angular-oauth2-oidc

Cap. 17 · 0 din 4 terminate

Ai terminat lecția?

Marcheaz-o și apare pe harta ta: în cuprins și pe prima pagină.

Se marchează singură după ce citești toată lecția.

  1. Securitate: XSS, sanitizare & CSP
  2. Autentificare: token-uri & refresh
  3. OAuth 2.1 și OpenID Connect cu PKCE
  4. Internaționalizare (i18n)
Vezi toată harta ta

Toate cele 75 de pagini · scrie ca să filtrezi

  • 01Ce e nou în Angular 21 & 22Toate schimbările importante din v21 și v22, pe scurt, cu link către pagina care le explică.Start
  • 01Prima ta aplicație, pas cu pasO listă de lecturi construită de la o componentă goală la o aplicație terminată — signals, @for, input-uri și output-uri, un serviciu și un formular, pas cu pas.Start
  • 01Decizii rapide„Unde pun asta?”, „Signal sau Observable?”, „Shared sau feature?” — răspunsuri scurte la întrebările de zi cu zi.Start
  • 01Întrebări de interviu Angular60 de întrebări reale de interviu pentru junior, mid și senior, cu răspunsul de dat, întrebarea care urmează și greșelile de evitat. Plus o simulare de interviu.Start
  • 02Standalone & bootstrapCum pornește o aplicație fără NgModule: bootstrapApplication, app.config.ts, imports în componentă.Fundamente moderne
  • 02Data binding și evenimenteInterpolare, binding pe proprietăți și pe atribute, evenimente cu $event și filtre de taste și cum e construit [( )] — ce intră în DOM și ce se întoarce.Fundamente moderne
  • 02Control flow: @if, @for, @switchSintaxa de blocuri care înlocuiește *ngIf/*ngFor/ngSwitch, cu track, @empty și verificare exhaustivă.Fundamente moderne
  • 02@let și expresiile noi din templateVariabile locale cu @let, arrow functions, spread, regex, instanceof, comentarii în tag-uri.Fundamente moderne
  • 02@defer — lazy loading în templateÎncarci componente grele doar când e nevoie: triggere, prefetch, placeholder, loading, error.Fundamente moderne
  • 02Class & style bindingsDe ce [class.x] și [style.x] au înlocuit ngClass și ngStyle (cu migrare automată din v21).Fundamente moderne
  • 02Stilurile componentelor și încapsularea:host, cele trei moduri de încapsulare, teme cu variabile CSS și de ce ::ng-deep e ultima soluție.Fundamente moderne
  • 02Pipes: când și cumPipe pur vs impur, pipe custom și cum alegi între pipe, computed() și o metodă.Fundamente moderne
  • 03signal() și computed()Primitivele reactive: stare scriibilă, stare derivată, egalitate, untracked și reguli de imutabilitate.Signals & reactivitate
  • 03effect() — când DA, când NUEfecte secundare sincronizate cu semnale: localStorage, logging, API-uri imperative. Și capcanele lor.Signals & reactivitate
  • 03linkedSignal() — stare derivată editabilăUn semnal care se resetează când se schimbă sursa, dar pe care îl poți și seta manual.Signals & reactivitate
  • 03resource, rxResource, httpResourceDate asincrone ca semnale: status, value, error, reload, anulare automată. Stabile din v22.Signals & reactivitate
  • 03debounced() — semnale cu întârziereVarianta „debounce” a unui semnal, fără RxJS. Perfect pentru căutare.Signals & reactivitate
  • 03Liste: căutare, filtrare, sortare, paginareUn lanț de computed-uri care recalculează doar ce s-a schimbat, cu filtrele ținute în URL, ca un link să redeschidă aceeași vedere.Signals & reactivitate
  • 03Observables de la zeroCe e un Observable, subscribe și unsubscribe, operatorii pe care îi folosești în fiecare săptămână, cold vs hot — și când ajunge un signal.Signals & reactivitate
  • 03Signals ↔ RxJStoSignal, toObservable, takeUntilDestroyed, outputFromObservable — și când RxJS rămâne alegerea bună.Signals & reactivitate
  • 04input() și output()Inputuri ca semnale (required, transform, alias) și outputuri tipizate. Data down, events up.Componente: comunicare
  • 04model() — two-way bindingBanana-in-a-box [( )] cu un singur API. Când îl folosești și când e mai bine input + output.Componente: comunicare
  • 04viewChild & contentChildInterogări ca semnale: viewChild, viewChildren, contentChild, contentChildren, opțiunea read.Componente: comunicare
  • 04Lifecycle & DestroyRefCe hook-uri mai contează în era signals, afterNextRender, DestroyRef și ngOnChanges tipizat.Componente: comunicare
  • 04Content projectionng-content cu select, sloturi multiple, conținut implicit și capcana proiecției condiționale.Componente: comunicare
  • 04ng-template & ngTemplateOutletTemplate-uri ca valori: render props în Angular, context tipizat și personalizare de componente.Componente: comunicare
  • 05Smart vs DumbContainer vs presentational: cine ține starea, cine doar afișează, și cum le împarți corect.Design de componente
  • 05Componente generice <T>Un DataTable<T> sau Select<T> tipizat corect, cu template-uri pentru celule.Design de componente
  • 05Componente dinamiceNgComponentOutlet, createComponent cu inputBinding/outputBinding și un dashboard construit din config.Design de componente
  • 05Directive & host compositionDirective atribut, metadata host: {} și Directive Composition API (hostDirectives).Design de componente
  • 05Controale custom de formularFormValueControl (Signal Forms) vs ControlValueAccessor: un rating cu stele, în ambele variante.Design de componente
  • 05Change detectionOnPush e implicit din v22, zone.js lipsește din v21. Ce declanșează randarea și cum să nu te arzi.Design de componente
  • 06inject() & @Service()Noul decorator @Service() din v22, inject() vs constructor și contextul de injecție.Dependency Injection
  • 06Providers & ierarhia de injectoriRoot vs rută vs componentă, useClass/useValue/useFactory/useExisting și modificatorii optional/self/skipSelf.Dependency Injection
  • 06InjectionToken & provideX()Configurare tipizată, funcții provideFeature(), makeEnvironmentProviders și inițializatori.Dependency Injection
  • 06injectAsync() — servicii lazyÎncarci un serviciu greu abia când ai nevoie de el, cu prefetch la idle.Dependency Injection
  • 07Structura de folderecore / shared / features, ce intră în fiecare și cum arată un feature pe dinăuntru.Arhitectura aplicației
  • 07Shared vs featureCând muți o componentă în shared și (mai ales) când NU. Checklist concret.Arhitectura aplicației
  • 07Interfețe & modeleInterfață inline sau fișier separat? DTO vs model de domeniu, type vs interface, unde le pui.Arhitectura aplicației
  • 07State management cu signalsStore-uri simple pe semnale, facade, stare locală vs globală și când îți trebuie NgRx.Arhitectura aplicației
  • 07Style guide & namingConvențiile oficiale actuale: nume de fișiere fără sufixe, protected, readonly, ordinea membrilor.Arhitectura aplicației
  • 07Barrels, aliasuri & granițeindex.ts, path aliases, API public pe feature și reguli de dependență care se pot verifica automat.Arhitectura aplicației
  • 07Erori: @boundary & ErrorHandlerNoul bloc @boundary din v22.2, ErrorHandler global, erori HTTP și erori din resource.Arhitectura aplicației
  • 08Routing modern & lazy loadingloadComponent, rute copil, parametri ca input-uri și moștenirea parametrilor (default nou în v22).Routing
  • 08Guards & resolverscanActivate, canMatch, canDeactivate și resolve ca funcții, cu redirect prin UrlTree / RedirectCommand.Routing
  • 09Signal Forms — bazeleFormulare construite pe semnale: form(), [formField], validare prin schemă. Stabile din v22.Formulare
  • 09Signal Forms — avansatValidare custom și cross-field, async/HTTP cu debounce, liste dinamice, disabled/hidden, scheme reutilizabile.Formulare
  • 09Reactive Forms tipizateFormGroup tipizat, nonNullable, FormArray — și cum conviețuiesc cu Signal Forms.Formulare
  • 10HttpClient, interceptors & API layerHttpClient fără setup (v21), Fetch implicit (v22), interceptori funcționali și stratul de API cu mapare DTO.HTTP & date
  • 11Performanță — checklistCe chiar contează: OnPush + signals, track, @defer, lazy routes, imagini, bundle și hidratare.Calitate & performanță
  • 11Testare cu VitestVitest e runner-ul implicit din v21. TestBed, componente cu semnale, servicii, HTTP și harness-uri.Calitate & performanță
  • 11DevTools: cum te uiți în aplicațieChrome DevTools și Angular DevTools pas cu pas: ce arată fiecare panou, cu exerciții care merg chiar pe site-ul ăsta.Calitate & performanță
  • 11Angular AriaComponente headless, accesibile, pe care le stilizezi cum vrei: tabs, accordion, menu, listbox...Calitate & performanță
  • 12SSR, prerender & render modesCând randezi pe server, când la build și când doar în browser: RenderMode pe rută, app.routes.server.ts și costurile reale.SSR & hidratare
  • 12Hidratare: event replay & incrementalCum preia browserul HTML-ul de pe server fără să-l refacă: hidratare completă, event replay, @defer (hydrate on …) și erorile de nepotrivire.SSR & hidratare
  • 12Cod compatibil cu serverulwindow nu există pe server: PLATFORM_ID, afterNextRender, DOCUMENT, TransferState, cache-ul HTTP și PendingTasks.SSR & hidratare
  • 13Micro frontendsCând merită să spargi o aplicație în mai multe deploy-uri și când e doar complexitate: Native Federation, Module Federation, iframes, Web Components.Arhitectură la scară
  • 13Web Components cu Angular ElementscreateCustomElement: o componentă Angular folosită ca tag HTML oriunde, plus comunicarea între aplicații prin evenimente și BroadcastChannel.Arhitectură la scară
  • 13Monorepo, librării & granițeUn workspace cu mai multe aplicații și librării: ng generate library, Nx, entry points secundare, publicare și reguli de dependență.Arhitectură la scară
  • 13Arhitectură hexagonală (ports & adapters)Domeniul fără Angular, porturi ca clase abstracte și adaptoare interschimbabile: HTTP, localStorage sau in-memory, fără să atingi UI-ul.Arhitectură la scară
  • 14Semnalele pe dinăuntruGraful reactiv: producători și consumatori, push-pull, de ce computed e lazy și glitch-free, problema diamantului și când rulează efectele.Reactivitate avansată
  • 14RxJS avansat: operatori de ordin superiorswitchMap, mergeMap, concatMap și exhaustMap pe o diagramă live, plus retry cu backoff, shareReplay și cum eviți scurgerile.Reactivitate avansată
  • 14NgRx SignalStoreState management la scară pe semnale: withState, withComputed, withMethods, rxMethod, entități și hooks — și când un store simplu e suficient.Reactivitate avansată
  • 14Mașini de stări cu signalsFără stări imposibile: o mașină tipizată pe signals, cu tranziții, guards și diagrama stărilor, live.Reactivitate avansată
  • 15WebSockets: timp real care rezistă rețeleiUn socket tipizat ca signals: reconectare cu backoff și jitter, heartbeat, coadă de mesaje cât ești offline și siguranță la SSR.Timp real și offline
  • 15Actualizări optimiste și rollbackInterfață instantanee, dar onestă: snapshot și rollback, răspunsuri sosite în altă ordine, chei de idempotență.Timp real și offline
  • 15Offline-first: Service Worker și cacheService Worker-ul Angular: strategii de cache, anunțarea versiunilor noi și o coadă pentru modificările făcute offline.Timp real și offline
  • 16Web WorkersCalcule grele fără să blochezi interfața: un worker real în pagină, comunicare prin mesaje și când nu merită.UI & performanță avansată
  • 16Liste uriașe: virtual scroll100.000 de rânduri fără lag: CDK virtual scroll, itemSize, trackBy și alternativele (paginare, infinite scroll).UI & performanță avansată
  • 16Animații moderne & View Transitionsanimate.enter și animate.leave în locul @angular/animations (depreciat), tranziții CSS și View Transitions la navigare.UI & performanță avansată
  • 16Memory leaks: cum le găsești și le repariSubscripții, timere și listeneri care trăiesc mai mult decât componenta lor — cum îi vezi în DevTools și cum îi închizi.UI & performanță avansată
  • 17Securitate: XSS, sanitizare & CSPCe face Angular automat împotriva XSS, când devine periculos bypassSecurityTrust, plus CSP cu nonce, Trusted Types și XSRF.Securitate & globalizare
  • 17Autentificare: token-uri & refreshUn interceptor care atașează token-ul, reîmprospătează o singură dată la 401 și reia cererile din coadă — plus unde ții token-ul.Securitate & globalizare
  • 17OAuth 2.1 și OpenID Connect cu PKCEFluxul authorization code cu PKCE, pas cu pas: verifier și challenge, state și nonce, ID token-ul și de ce un BFF e mai sigur.Securitate & globalizare
  • 17Internaționalizare (i18n)$localize, atributul i18n, ICU plural/select, build per limbă vs. traduceri la runtime și formatarea după locale.Securitate & globalizare