use alloc::str::Utf8Error;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ParseError {
#[error("input is missing a required part")]
MissingPart,
#[error("final terminator is missing. Incomplete input?")]
MissingEnd,
#[error("required 'auth' field is missing")]
MissingToken,
#[error("gs2-header is invalid")]
InvalidGs2,
#[error("OAUTHBEARER can't support channel bindings")]
ChannelBindings,
#[error("K/V pair is invalid")]
InvalidKVPair,
#[error("Invalid UTF-8")]
InvalidUtf8(
#[from]
#[source]
Utf8Error,
),
}
pub struct OAuthBearerMsg<'a> {
pub authzid: Option<&'a str>,
pub token: &'a str,
pub fields: Vec<(&'a str, &'a str)>,
}
impl<'a> OAuthBearerMsg<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, ParseError> {
if let Some(bytes) = bytes.strip_suffix(b"\x01\x01") {
let mut it = bytes.split(|b| matches!(b, b'\x01'));
let gs2 = it.next().ok_or(ParseError::MissingPart)?;
let mut gs2iter = gs2.split(|b| matches!(b, b','));
let gs2_first = gs2iter.next().ok_or(ParseError::InvalidGs2)?;
if gs2_first != b"n" {
return Err(ParseError::ChannelBindings);
}
let gs2_authzid = gs2iter.next().ok_or(ParseError::InvalidGs2)?;
let authzid = if gs2_authzid.is_empty() {
None
} else if let Some(rem) = gs2_authzid.strip_prefix(b"a=") {
Some(core::str::from_utf8(rem)?)
} else {
return Err(ParseError::InvalidGs2);
};
let mut token = None;
let mut fields = Vec::new();
for kv in it {
if let Some(t) = kv.strip_prefix(b"auth=") {
token = Some(core::str::from_utf8(t)?);
} else {
let mut kviter = kv.split(|b| matches!(b, b'='));
let key = kviter.next().ok_or(ParseError::InvalidKVPair)?;
let val = kviter.next().ok_or(ParseError::InvalidKVPair)?;
if kviter.next().is_some() {
return Err(ParseError::InvalidKVPair);
}
let key = core::str::from_utf8(key)?;
let val = core::str::from_utf8(val)?;
fields.push((key, val));
}
}
let token = token.ok_or(ParseError::MissingToken)?;
Ok(Self {
authzid,
token,
fields,
})
} else {
Err(ParseError::MissingEnd)
}
}
}