state chống CSRF trên bước callback. Không có nó, kẻ tấn công có thể lấy authorization code của tài khoản của chính hắn rồi dụ nạn nhân mở link callback đó — kết quả là tài khoản của nạn nhân bị gắn (link) với tài khoản provider của kẻ tấn công, hoặc nạn nhân vô tình thao tác dưới danh nghĩa tài khoản lạ.
Cách làm đúng: sinh chuỗi ngẫu nhiên, lưu vào session/cookie phía server, gửi kèm request authorize, và khi callback thì so lại. Không khớp thì hủy luồng.
const state = crypto.randomUUID()
session.oauthState = state
redirect(`${authorizeUrl}?...&state=${state}`)
// callback: if (query.state !== session.oauthState) throw new Error('state mismatch')nonce là của OIDC, phục vụ chuyện khác: nó được nhúng vào id_token và ràng token đó với đúng request đăng nhập đã phát ra — chống replay một id_token cũ.
Gói gọn sự khác biệt:
state— bảo vệ luồng redirect, kiểm ở phía client/server app, không xuất hiện trong token.nonce— bảo vệ token, được provider đưa vào claim củaid_tokenđể bạn đối chiếu.
Dùng PKCE rồi vẫn nên giữ state nếu cần chống CSRF; PKCE ràng buộc code với client, còn state ràng buộc luồng với phiên trình duyệt.