use std::error::Error;
use std::fmt;
use std::str;
lazy_static! {
static ref BASE64_CONFIG: base64::Config =
base64::Config::new(base64::CharacterSet::UrlSafe, false);
}
#[derive(Debug)]
pub struct Token {
pub header: serde_json::Value,
pub body: serde_json::Value,
pub signature: Vec<u8>,
}
impl Token {
fn new(header: serde_json::Value, body: serde_json::Value, signature: Vec<u8>) -> Self {
Self {
header,
body,
signature,
}
}
}
impl str::FromStr for Token {
type Err = JWTParsePartError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse(s)
}
}
#[derive(Debug)]
pub enum JWTParseError {
MissingSection(),
InvalidUtf8(str::Utf8Error),
InvalidBase64(base64::DecodeError),
InvalidJSON(serde_json::error::Error),
}
impl fmt::Display for JWTParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let message = match self {
JWTParseError::MissingSection() => "Missing token section".to_string(),
JWTParseError::InvalidUtf8(e) => format!("UTF8 error, {}", e),
JWTParseError::InvalidBase64(e) => format!("Base64 error, {}", e),
JWTParseError::InvalidJSON(e) => format!("JSON error, {}", e),
};
write!(f, "{}", message)
}
}
impl From<str::Utf8Error> for JWTParseError {
fn from(err: str::Utf8Error) -> JWTParseError {
JWTParseError::InvalidUtf8(err)
}
}
impl From<base64::DecodeError> for JWTParseError {
fn from(err: base64::DecodeError) -> JWTParseError {
JWTParseError::InvalidBase64(err)
}
}
impl From<serde_json::error::Error> for JWTParseError {
fn from(err: serde_json::error::Error) -> JWTParseError {
JWTParseError::InvalidJSON(err)
}
}
#[derive(Debug)]
pub enum JWTParsePartError {
Header(JWTParseError),
Body(JWTParseError),
Signature(JWTParseError),
UnexpectedPart(),
}
impl fmt::Display for JWTParsePartError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let message = match self {
JWTParsePartError::Header(e) => format!("Invalid Header: {}", e),
JWTParsePartError::Body(e) => format!("Invalid Body: {}", e),
JWTParsePartError::Signature(e) => format!("Invalid Signature: {}", e),
JWTParsePartError::UnexpectedPart() => {
"Error: Unexpected fragment after signature".to_string()
}
};
write!(f, "{}", message)
}
}
impl Error for JWTParsePartError {}
#[doc(hidden)]
fn parse_base64_string(s: &str) -> Result<String, JWTParseError> {
let s = base64::decode_config(s, *BASE64_CONFIG)?;
let s = str::from_utf8(&s)?;
Ok(s.to_string())
}
#[doc(hidden)]
fn parse_header(raw_header: Option<&str>) -> Result<serde_json::Value, JWTParseError> {
match raw_header {
None => Err(JWTParseError::MissingSection()),
Some(s) => {
let header_str = parse_base64_string(s)?;
Ok(serde_json::from_str(&header_str)?)
}
}
}
#[doc(hidden)]
fn parse_body(raw_body: Option<&str>) -> Result<serde_json::Value, JWTParseError> {
match raw_body {
None => Err(JWTParseError::MissingSection()),
Some(s) => {
let body_str = parse_base64_string(s)?;
Ok(serde_json::from_str(&body_str)?)
}
}
}
#[doc(hidden)]
fn parse_signature(raw_signature: Option<&str>) -> Result<Vec<u8>, JWTParseError> {
match raw_signature {
None => Err(JWTParseError::MissingSection()),
Some(s) => Ok(base64::decode_config(s, *BASE64_CONFIG)?),
}
}
pub fn parse<T: AsRef<str>>(token: T) -> Result<Token, JWTParsePartError> {
let mut parts = token.as_ref().split('.');
let header = parse_header(parts.next()).map_err(JWTParsePartError::Header)?;
let body = parse_body(parts.next()).map_err(JWTParsePartError::Body)?;
let signature = parse_signature(parts.next()).map_err(JWTParsePartError::Signature)?;
if parts.next().is_some() {
return Err(JWTParsePartError::UnexpectedPart());
}
Ok(Token::new(header, body, signature))
}
#[cfg(test)]
mod test;