use crate::authn::ids::UserId;
use crate::session::refresh::{RefreshToken, RefreshTokenId, RefreshTokenStore, TokenFamilyId};
use crate::store::{MemoryStore, Store};
use chrono::Utc;
use std::time::Duration;
#[derive(Clone, Default)]
pub struct MemoryRefreshTokenStore {
inner: MemoryStore<RefreshTokenId, RefreshToken>,
}
pub type MemoryRefreshStoreError = std::convert::Infallible;
impl MemoryRefreshTokenStore {
pub fn new() -> Self {
Self::default()
}
}
impl RefreshTokenStore for MemoryRefreshTokenStore {
type Error = MemoryRefreshStoreError;
async fn store_token(&self, token: &RefreshToken) -> Result<(), Self::Error> {
let ttl = (token.expires_at - Utc::now())
.to_std()
.unwrap_or(Duration::ZERO);
self.inner.put(&token.id, token, ttl).await
}
async fn find_token(&self, token_hash: &str) -> Result<Option<RefreshToken>, Self::Error> {
let found = self
.inner
.snapshot()
.into_iter()
.find(|(_, t)| t.token_hash == token_hash)
.map(|(_, t)| t);
Ok(found)
}
async fn revoke_token(&self, token_id: &RefreshTokenId) -> Result<(), Self::Error> {
self.inner.update(token_id, |t| t.revoked = true);
Ok(())
}
async fn revoke_user_tokens(&self, user_id: &UserId) -> Result<(), Self::Error> {
for (id, token) in self.inner.snapshot() {
if &token.user_id == user_id {
self.inner.update(&id, |t| t.revoked = true);
}
}
Ok(())
}
async fn revoke_family(
&self,
user_id: &UserId,
family_id: &TokenFamilyId,
) -> Result<(), Self::Error> {
for (id, token) in self.inner.snapshot() {
if &token.user_id == user_id && token.family_id.as_ref() == Some(family_id) {
self.inner.update(&id, |t| t.revoked = true);
}
}
Ok(())
}
async fn active_tokens(&self, user_id: &UserId) -> Result<Vec<RefreshToken>, Self::Error> {
let mut tokens: Vec<RefreshToken> = self
.inner
.snapshot()
.into_iter()
.filter(|(_, t)| &t.user_id == user_id && !t.revoked)
.map(|(_, t)| t)
.collect();
tokens.sort_by_key(|t| t.issued_at);
Ok(tokens)
}
async fn issue_with_eviction(
&self,
evict_ids: &[RefreshTokenId],
new_token: &RefreshToken,
) -> Result<(), Self::Error> {
for id in evict_ids {
self.revoke_token(id).await?;
}
self.store_token(new_token).await
}
async fn rotate_token(
&self,
parent_id: &RefreshTokenId,
new_token: &RefreshToken,
) -> Result<(), Self::Error> {
self.revoke_token(parent_id).await?;
self.store_token(new_token).await
}
}