"""Opaque refresh tokens (FR-ACC-05, 架构文档 §6.2 id_session / §9.1). 为什么是 opaque 随机串而不是 JWT:refresh 的核心价值就是「服务端可单条撤销」 (FR-ACC-05),无状态 JWT 天生做不到撤销——除非引入黑名单,而黑名单本身就是把 状态搬回服务端,那不如直接用查表就能撤销的 opaque 令牌。 库里只存 sha256 哈希而非明文:查库校验时重算一次哈希即可比对,而泄漏数据库 不等于泄漏令牌——sha256 不可逆,拿到哈希换不出新 access。 本模块是纯函数(无 IO、无配置),方便穷举式单测。 """ from __future__ import annotations import hashlib import secrets #: token_urlsafe(48) 输出 64 个 URL 安全字符,携带 384 bit 熵, #: 暴力枚举哈希空间在计算上不可行。 _TOKEN_BYTES = 48 def generate_refresh_token() -> str: """生成新 refresh token。明文只在签发瞬间出现一次,此后系统内只见哈希。""" return secrets.token_urlsafe(_TOKEN_BYTES) def hash_refresh_token(token: str) -> str: """sha256 hex(64 字符,与 id_session.refresh_token_hash 列宽一致)。""" return hashlib.sha256(token.encode()).hexdigest()