PKCE bắt buộc cho SPA/mobile (không có client secret); access token trong memory, refresh token trong httpOnly cookie — không bao giờ localStorage; BFF pattern là gold standard cho SPAs vì server giữ tokens hoàn toàn.
OAuth 2.0 có nhiều security pitfalls nếu implement không đúng. PKCE (Proof Key for Code Exchange): bắt buộc cho public clients (SPA, mobile app không có server-side). Flow: app tạo code_verifier (random 43-128 chars), tính code_challenge = base64url(sha256(code_verifier)), gửi challenge trong authorization request; khi exchange code → token, gửi verifier; server verify sha256(verifier) === challenge — ngăn authorization code interception. State parameter: random string trong authorization request, verify sau redirect — ngăn CSRF attack trên OAuth flow. Token storage SPA: access token trong memory (JS variable) — không persist qua page refresh, an toàn nhất; refresh token trong httpOnly cookie — không readable từ JS; KHÔNG dùng localStorage (XSS đánh cắp được). Backend For Frontend (BFF) pattern: SPA gọi BFF server, BFF handle OAuth flow và lưu tokens server-side — SPA không bao giờ chạm vào tokens. Scope limitation: request chỉ scopes cần thiết (principle of least privilege); users thấy permission prompt với ít permissions hơn → higher consent rate. Token binding: bind token đến client TLS certificate để ngăn token theft (draft standard). Lưu ý: không validate redirect_uri strictly — partial match cho phép attacker redirect token đến subdomain độc hại.
PKCE is required for SPAs/mobile apps (no client secret); access token in memory, refresh token in httpOnly cookie — never localStorage; BFF pattern is the gold standard for SPAs as the server holds tokens entirely.
OAuth 2.0 has many security pitfalls if not implemented correctly. PKCE (Proof Key for Code Exchange): required for public clients (SPAs, mobile apps without a server side). Flow: app creates code_verifier (random 43-128 chars), computes code_challenge = base64url(sha256(code_verifier)), sends the challenge in the authorization request; when exchanging the code for a token, sends the verifier; server verifies sha256(verifier) === challenge — prevents authorization code interception. State parameter: random string in the authorization request, verified after redirect — prevents CSRF attacks on the OAuth flow. Token storage for SPAs: access token in memory (JS variable) — not persisted across page refreshes, safest; refresh token in an httpOnly cookie — not readable from JS; NEVER use localStorage (XSS can steal it). Backend For Frontend (BFF) pattern: SPA calls a BFF server, BFF handles the OAuth flow and stores tokens server-side — SPA never touches tokens. Scope limitation: request only necessary scopes (principle of least privilege); users see a permission prompt with fewer permissions → higher consent rate. Pitfall: not validating redirect_uri strictly — partial matching allows attackers to redirect tokens to a malicious subdomain.