Rotation: mỗi lần dùng refresh token để lấy access token mới, server phát ra refresh token mới và vô hiệu hóa cái cũ ngay lập tức. Refresh token trở thành dùng-một-lần.
Reuse detection: nếu một refresh token đã bị thay thế lại được đem đi dùng, hệ thống coi đó là dấu hiệu token bị đánh cắp và thu hồi cả họ token (token family) — tức mọi refresh token bắt nguồn từ lần đăng nhập đó. User bị buộc đăng nhập lại.
Vì sao cần cả hai: chỉ rotation thôi không phát hiện được kẻ trộm. Nếu kẻ tấn công lấy được RT1 và dùng trước, hắn có RT2 còn user cầm RT1 đã chết — user chỉ thấy lỗi đăng nhập, hệ thống không biết gì. Reuse detection biến đúng cái xung đột đó thành tín hiệu: token cũ được tái sử dụng nghĩa là có hai bên cùng cầm token.
Schema tối thiểu:
create table refresh_tokens (
id uuid primary key,
family_id uuid not null, -- one login session
user_id uuid not null,
token_hash text not null, -- store the hash, never the raw token
replaced_by uuid, -- non-null => already rotated
revoked_at timestamptz,
expires_at timestamptz not null
);Luồng khi refresh: tra theo token_hash → nếu revoked_at hoặc replaced_by khác null thì update ... set revoked_at = now() where family_id = ? rồi trả 401.
Hai chi tiết dễ bị hỏi thêm: lưu hash chứ không lưu token thô (rò DB thì token vẫn vô dụng); và cần xử lý race condition khi nhiều tab cùng refresh một lúc — thường cho phép một khoảng ân hạn vài giây cho token vừa bị thay, hoặc khóa theo hàng ở DB.