use crate::error::AuthError;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JwtClaims {
#[serde(skip)]
pub sub: String,
pub roles: Vec<String>,
pub email: String,
#[serde(default)]
pub name: Option<String>,
#[serde(flatten)]
pub custom: serde_json::Map<String, serde_json::Value>,
}
pub fn parse_claims_unverified(token: &str) -> Result<JwtClaims, AuthError> {
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 {
return Err(AuthError::Verification("invalid JWT format: expected 3 dot-separated segments".into()));
}
let payload_bytes =
URL_SAFE_NO_PAD.decode(parts[1]).map_err(|e| AuthError::Verification(format!("failed to decode JWT payload: {e}")))?;
let payload: serde_json::Value =
serde_json::from_slice(&payload_bytes).map_err(|e| AuthError::Verification(format!("failed to parse JWT payload JSON: {e}")))?;
let sub = payload
.get("sub")
.and_then(|v| v.as_str())
.ok_or_else(|| AuthError::Verification("missing 'sub' claim in JWT payload".into()))?
.to_string();
let roles = payload
.get("roles")
.and_then(|v| v.as_array())
.map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect())
.unwrap_or_default();
let email = payload.get("email").and_then(|v| v.as_str()).unwrap_or_default().to_string();
let name = payload.get("name").and_then(|v| v.as_str()).map(String::from);
let known_keys: &[&str] = &["sub", "roles", "email", "name", "iat", "exp", "nbf", "iss", "aud", "jti"];
let custom = if let Some(obj) = payload.as_object() {
obj.iter().filter(|(k, _)| !known_keys.contains(&k.as_str())).map(|(k, v)| (k.clone(), v.clone())).collect()
} else {
serde_json::Map::new()
};
Ok(JwtClaims { sub, roles, email, name, custom })
}