Skip to content
Clone
a06673b
46 lines 1.4 KB Rust
use std::sync::Arc;
use cache::redis::RedisError;
use cache::RedisCache;
use uuid::Uuid;
/// Redis-backed deny-list for access tokens.
///
/// Two levels of revocation:
/// - per-token: `auth:deny:jti:{jti}` — a single access token is dead.
/// - per-user: `auth:deny:user:{sub}` — all tokens for a user are dead
/// (used on password change, suspension, full session revocation).
#[derive(Clone)]
pub struct AccessTokenBlacklist {
kv: Arc<RedisCache>,
}
impl AccessTokenBlacklist {
pub fn new(kv: Arc<RedisCache>) -> Self {
Self { kv }
}
pub async fn revoke_jti(&self, jti: Uuid, ttl_seconds: u64) -> Result<(), RedisError> {
let key = format!("auth:deny:jti:{}", jti.simple());
self.kv.set_bytes(&key, b"1".to_vec(), ttl_seconds).await
}
pub async fn revoke_user(&self, user_id: Uuid, ttl_seconds: u64) -> Result<(), RedisError> {
let key = format!("auth:deny:user:{}", user_id.simple());
self.kv.set_bytes(&key, b"1".to_vec(), ttl_seconds).await
}
pub async fn is_revoked(
&self,
jti: Uuid,
sub: Uuid,
) -> Result<bool, RedisError> {
let jti_key = format!("auth:deny:jti:{}", jti.simple());
let user_key = format!("auth:deny:user:{}", sub.simple());
let jti_hit = self.kv.get_bytes(&jti_key).await?.is_some();
if jti_hit {
return Ok(true);
}
Ok(self.kv.get_bytes(&user_key).await?.is_some())
}
}