use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct JwtPayload {
#[serde(default)]
pub sub: Option<String>,
#[serde(default)]
pub iss: Option<String>,
#[serde(default)]
pub exp: Option<u64>,
#[serde(default)]
pub iat: Option<u64>,
#[serde(default)]
pub aud: Option<String>,
}
#[derive(Clone, PartialEq, Eq)]
pub struct Jwt {
pub raw: String,
pub payload: JwtPayload,
}
impl std::fmt::Debug for Jwt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Jwt")
.field("raw", &"[redacted]")
.field("payload", &self.payload)
.finish()
}
}
impl Jwt {
pub fn decode(token: &str) -> Option<Self> {
let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 {
return None;
}
let payload_b64 = parts[1];
let payload_bytes = decode_base64url(payload_b64)?;
let payload: JwtPayload = serde_json::from_slice(&payload_bytes).ok()?;
Some(Self {
raw: token.to_string(),
payload,
})
}
pub fn register_bearer(auth_value: &str, headers: &mut Vec<(String, String)>) -> Option<Self> {
let token = if let Some(stripped) = auth_value.strip_prefix("Bearer ") {
stripped.trim()
} else {
auth_value.trim()
};
if let Some(jwt) = Self::decode(token) {
headers.push(("Authorization".to_string(), format!("Bearer {}", token)));
Some(jwt)
} else {
None
}
}
}
fn decode_base64url(input: &str) -> Option<Vec<u8>> {
let mut out = Vec::with_capacity((input.len() * 3) / 4);
let mut buffer = 0u32;
let mut bits = 0;
for b in input.bytes() {
let val = match b {
b'A'..=b'Z' => b - b'A',
b'a'..=b'z' => b - b'a' + 26,
b'0'..=b'9' => b - b'0' + 52,
b'-' => 62,
b'_' => 63,
b'=' => continue, _ => return None,
};
buffer = (buffer << 6) | (val as u32);
bits += 6;
if bits >= 8 {
bits -= 8;
out.push((buffer >> bits) as u8);
}
}
Some(out)
}