use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub user_id: u64,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub profile_picture: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenIntrospection {
pub active: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub token_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exp: Option<i64>,
}
impl TokenIntrospection {
pub fn is_active(&self) -> bool {
self.active
}
pub fn scopes(&self) -> Vec<String> {
self.scope
.as_ref()
.map(|s| s.split_whitespace().map(String::from).collect())
.unwrap_or_default()
}
pub fn has_scope(&self, scope: &str) -> bool {
self.scopes().iter().any(|s| s == scope)
}
pub fn is_expired(&self) -> bool {
let Some(exp) = self.exp else {
return false;
};
let Ok(duration) = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)
else {
return false;
};
duration.as_secs() as i64 >= exp
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_scopes() {
let token = TokenIntrospection {
active: true,
client_id: Some("test".to_string()),
token_type: Some("Bearer".to_string()),
scope: Some("user:read channel:read".to_string()),
exp: Some(9999999999),
};
assert_eq!(token.scopes(), vec!["user:read", "channel:read"]);
assert!(token.has_scope("user:read"));
assert!(token.has_scope("channel:read"));
assert!(!token.has_scope("chat:write"));
}
#[test]
fn test_token_expiry() {
let expired = TokenIntrospection {
active: true,
client_id: Some("test".to_string()),
token_type: Some("Bearer".to_string()),
scope: Some("user:read".to_string()),
exp: Some(0), };
assert!(expired.is_expired());
let valid = TokenIntrospection {
active: true,
client_id: Some("test".to_string()),
token_type: Some("Bearer".to_string()),
scope: Some("user:read".to_string()),
exp: Some(9999999999), };
assert!(!valid.is_expired());
}
}