use crate::types::User;
use base64::Engine;
use serde_json::Value;
use std::sync::RwLock;
use tokio::sync::broadcast;
pub fn decode_jwt_payload(token: &str) -> Option<Value> {
let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 {
return None;
}
let mut payload = parts[1].to_string();
payload = payload.replace('-', "+").replace('_', "/");
while !payload.len().is_multiple_of(4) {
payload.push('=');
}
let decoded = base64::engine::general_purpose::STANDARD
.decode(payload)
.ok()?;
let json_str = String::from_utf8(decoded).ok()?;
serde_json::from_str(&json_str).ok()
}
pub fn is_token_expired(token: &str, margin_sec: i64) -> bool {
if let Some(payload) = decode_jwt_payload(token) {
if let Some(exp) = payload.get("exp").and_then(|v| v.as_i64()) {
let now = chrono::Utc::now().timestamp();
return exp - margin_sec <= now;
}
}
true
}
pub trait AuthStore: Send + Sync + std::fmt::Debug {
fn token(&self) -> Option<String>;
fn refresh_token(&self) -> Option<String>;
fn user(&self) -> Option<User>;
fn is_valid(&self) -> bool;
fn save(&self, token: String, refresh_token: String, user: User);
fn clear(&self);
fn subscribe(&self) -> broadcast::Receiver<(Option<String>, Option<User>)>;
}
#[derive(Debug)]
pub struct InMemoryAuthStore {
token: RwLock<Option<String>>,
refresh_token: RwLock<Option<String>>,
user: RwLock<Option<User>>,
tx: broadcast::Sender<(Option<String>, Option<User>)>,
}
impl Default for InMemoryAuthStore {
fn default() -> Self {
let (tx, _) = broadcast::channel(32);
Self {
token: RwLock::new(None),
refresh_token: RwLock::new(None),
user: RwLock::new(None),
tx,
}
}
}
impl InMemoryAuthStore {
pub fn new() -> Self {
Self::default()
}
}
impl AuthStore for InMemoryAuthStore {
fn token(&self) -> Option<String> {
self.token.read().unwrap().clone()
}
fn refresh_token(&self) -> Option<String> {
self.refresh_token.read().unwrap().clone()
}
fn user(&self) -> Option<User> {
self.user.read().unwrap().clone()
}
fn is_valid(&self) -> bool {
if let Some(tok) = self.token() {
!is_token_expired(&tok, 0)
} else {
false
}
}
fn save(&self, token: String, refresh_token: String, user: User) {
*self.token.write().unwrap() = Some(token.clone());
*self.refresh_token.write().unwrap() = Some(refresh_token);
*self.user.write().unwrap() = Some(user.clone());
let _ = self.tx.send((Some(token), Some(user)));
}
fn clear(&self) {
*self.token.write().unwrap() = None;
*self.refresh_token.write().unwrap() = None;
*self.user.write().unwrap() = None;
let _ = self.tx.send((None, None));
}
fn subscribe(&self) -> broadcast::Receiver<(Option<String>, Option<User>)> {
self.tx.subscribe()
}
}