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). Pitfall: không validate redirect_uri strictly — partial match cho phép attacker redirect token đến subdomain độc hại.