// refresh.mbt — G1 旋转核:refresh 换发 + 重放检测撤全族(D-G3)
//
// 账本语义:store.get(jti) 无记录 = mac 合法但不出自本账本(伪造);
// used 再现 = 重放,立即 revoke_family;revoked = 族已死,新老皆拒。
// 换发只管 refresh 串;access 续发是纯函数 issue(),消费方随手调。
///|
/// 换发错误。Verify(Expired) 即"回握手"信号(A0 语义,由消费方解释)。
pub(all) enum RefreshErr {
Verify(VerifyErr)
UnknownToken
Replayed(String)
Revoked(Claims)
} derive(Eq, Debug)
///|
/// 换发产物:新 refresh 票 + 族号 + 主体。
pub(all) struct Refreshed {
refresh : Issued
family_id : String
sub : String
} derive(Eq, Debug)
///|
/// 换发:verify_for(refresh) → 账本核 → 旧票 mark_used → 新票入族。
/// 纯函数 + 账本参数:时间与熵(next_jti)全由消费方注入(D-G2),
/// 核心零时钟零随机。
pub fn refresh(
config : GuardConfig,
signer : &Signer,
store : &RefreshStore,
now : Int64,
token : String,
next_jti : String,
) -> Result[Refreshed, RefreshErr] {
let old = match verify_for(config, signer, now, token, TokenType::Refresh) {
Ok(c) => c
Err(e) => return Err(RefreshErr::Verify(e))
}
let record = match store.get(old.jti) {
Some(r) => r
None => return Err(RefreshErr::UnknownToken)
}
if record.revoked {
return Err(RefreshErr::Revoked(old))
}
if record.used {
store.revoke_family(record.family_id)
return Err(RefreshErr::Replayed(record.family_id))
}
store.mark_used(old.jti)
let issued = issue(config, signer, now, old.sub, TokenType::Refresh, next_jti)
store.put(next_jti, {
family_id: record.family_id,
sub: old.sub,
issued_at: now,
expires_at: issued.claims.exp,
used: false,
revoked: false,
})
Ok({ refresh: issued, family_id: record.family_id, sub: old.sub, })
}