Skip to content
Clone
a06673b
77 lines 2.7 KB Rust
use db::conn::sea_orm::EntityTrait;
use db::user::users::Entity as UserEntity;
use jwt::handlers::TokenPairResponse;
use jwt::rotation::{RotationContext, UserSnapshot, rotate_refresh};
use jwt::signer::{decode_refresh_token, extract_kid};
use crate::error::AuthError;
use crate::features::AuthService;
use super::{RefreshCmd, RefreshResp};
pub async fn refresh(cmd: RefreshCmd, svc: &AuthService) -> Result<RefreshResp, AuthError> {
let db = svc.db.writer();
let cache = svc.jwt.cache.read().await;
// VULN-007: decode the refresh token to learn user_id, then look up
// the live passwd_version so rotate_refresh can reject a token whose
// claim lags behind a password reset.
let user_id = {
let kid = extract_kid(&cmd.refresh_token)
.ok_or_else(|| AuthError::Unauthorized("invalid refresh token".into()))?;
let cached = cache
.get(&kid)
.ok_or_else(|| AuthError::Unauthorized("unknown signing key".into()))?;
let claims = decode_refresh_token(
&cmd.refresh_token,
&cached.decoding,
cached.algorithm,
&svc.jwt.settings.issuer,
&svc.jwt.settings.audience,
svc.jwt.settings.clock_skew_seconds,
)
.map_err(AuthError::Jwt)?;
claims.sub
};
let user = UserEntity::find_by_id(user_id)
.one(&db)
.await?
.ok_or_else(|| AuthError::Unauthorized("unknown user".into()))?;
// VULN-002/B4: suspended / restricted / deactivated accounts must not be
// able to keep their session alive through refresh. Mirror the login gate
// so a suspended user cannot mint fresh token pairs indefinitely.
if user.is_restricted {
return Err(AuthError::Forbidden("account restricted".into()));
}
if user.prohibit_login {
return Err(AuthError::Forbidden("login prohibited".into()));
}
if !user.is_active {
return Err(AuthError::Forbidden(
"account inactive, please verify your email".into(),
));
}
let current_pwd_version = user.passwd_version.unwrap_or(0);
let ctx = RotationContext {
db: &db,
settings: &svc.jwt.settings,
cache: &cache,
};
let snapshot = UserSnapshot {
pwd_version: current_pwd_version,
username: user.username.clone(),
is_admin: user.is_admin,
is_restricted: user.is_restricted,
};
let pair: TokenPairResponse =
rotate_refresh(&ctx, &cmd.refresh_token, &snapshot, cmd.app_id)
.await?
.into();
Ok(RefreshResp {
access_token: pair.access_token,
refresh_token: pair.refresh_token,
token_type: pair.token_type,
expires_in: pair.expires_in,
})
}