use crate::{error::AuthError, jwks::JwksCache, parser::JwtParser};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub exp: usize,
pub iat: Option<usize>,
pub jti: Option<String>,
pub email: Option<String>,
pub phone: Option<String>,
pub role: Option<String>,
pub app_metadata: Option<serde_json::Value>,
pub user_metadata: Option<serde_json::Value>,
pub aud: Option<String>,
pub iss: Option<String>,
pub aal: Option<String>,
pub amr: Option<Vec<serde_json::Value>>,
pub session_id: Option<String>,
pub is_anonymous: Option<bool>,
#[serde(skip)]
pub kid: Option<String>,
}
impl Claims {
pub async fn from_token(token: &str, jwks_cache: &JwksCache) -> Result<Self, AuthError> {
let jwt_header = JwtParser::decode_header(token)?;
let kid = jwt_header.kid.ok_or(AuthError::InvalidToken)?;
let jwk = jwks_cache.find_key(&kid).await?;
let decoding_key = JwtParser::create_decoding_key(&jwk)?;
let algorithm = JwtParser::parse_algorithm(&jwt_header.alg)?;
let mut claims = JwtParser::verify_and_decode(token, &decoding_key, algorithm)?;
claims.kid = Some(kid);
claims.validate_security()?;
Ok(claims)
}
pub async fn from_bearer_token(
bearer_token: &str,
jwks_cache: &JwksCache,
) -> Result<Self, AuthError> {
let token = bearer_token
.strip_prefix("Bearer ")
.ok_or(AuthError::MalformedToken)?;
Self::from_token(token, jwks_cache).await
}
pub fn validate_security(&self) -> Result<(), AuthError> {
if self.sub.trim().is_empty() {
return Err(AuthError::InvalidClaims);
}
Ok(())
}
}
impl Claims {
pub fn user_id(&self) -> &str {
&self.sub
}
pub fn email(&self) -> Option<&str> {
self.email.as_deref()
}
pub fn role(&self) -> &str {
self.role.as_deref().unwrap_or("authenticated")
}
pub fn phone(&self) -> Option<&str> {
self.phone.as_deref()
}
pub fn is_anonymous(&self) -> bool {
self.is_anonymous.unwrap_or(false)
}
}
impl Claims {
pub fn get_user_metadata<T>(&self, key: &str) -> Option<T>
where
T: serde::de::DeserializeOwned,
{
self.user_metadata
.as_ref()
.and_then(|metadata| metadata.get(key))
.and_then(|value| serde_json::from_value(value.clone()).ok())
}
pub fn get_app_metadata<T>(&self, key: &str) -> Option<T>
where
T: serde::de::DeserializeOwned,
{
self.app_metadata
.as_ref()
.and_then(|metadata| metadata.get(key))
.and_then(|value| serde_json::from_value(value.clone()).ok())
}
}