use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD as B64};
use crate::engine::shared_error::SharedAuthError;
fn check_strict_base64url(s: &str) -> Result<(), SharedAuthError> {
if s.bytes().any(|b| matches!(b, b'+' | b'/' | b'=')) {
return Err(SharedAuthError::LaxBase64);
}
Ok(())
}
fn check_no_duplicate_top_keys(bytes: &[u8]) -> Result<(), SharedAuthError> {
struct UniqueKeysVisitor;
impl<'de> serde::de::Visitor<'de> for UniqueKeysVisitor {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a JSON object with unique keys")
}
fn visit_map<M: serde::de::MapAccess<'de>>(self, mut access: M) -> Result<(), M::Error> {
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
while let Some(key) = access.next_key::<String>()? {
if !seen.insert(key) {
return Err(serde::de::Error::custom("duplicate key"));
}
let _: serde::de::IgnoredAny = access.next_value()?;
}
Ok(())
}
}
let mut deser = serde_json::Deserializer::from_slice(bytes);
use serde::de::Deserializer;
deser
.deserialize_map(UniqueKeysVisitor)
.map_err(|_| SharedAuthError::DuplicateJsonKeys)
}
pub(crate) fn parse_header_json(token: &str) -> Result<serde_json::Value, SharedAuthError> {
let header_b64 = token
.split('.')
.next()
.ok_or(SharedAuthError::NotJwsCompact)?;
check_strict_base64url(header_b64)?;
let bytes = B64
.decode(header_b64)
.map_err(|_| SharedAuthError::HeaderUnparseable)?;
check_no_duplicate_top_keys(&bytes)?;
serde_json::from_slice(&bytes).map_err(|_| SharedAuthError::HeaderUnparseable)
}
pub(crate) fn parse_payload_json(token: &str) -> Result<serde_json::Value, SharedAuthError> {
let payload_b64 = token
.split('.')
.nth(1)
.ok_or(SharedAuthError::NotJwsCompact)?;
check_strict_base64url(payload_b64)?;
let bytes = B64
.decode(payload_b64)
.map_err(|_| SharedAuthError::PayloadUnparseable)?;
check_no_duplicate_top_keys(&bytes)?;
serde_json::from_slice(&bytes).map_err(|_| SharedAuthError::PayloadUnparseable)
}