mod compact;
pub use compact::{Unverified, Verified};
use serde::de::DeserializeOwned;
use crate::errors::Error;
use crate::jwa::sign;
use crate::jwk;
use crate::{FromCompactPart, ToCompactPart};
use serde::{self, Deserialize, Serialize};
use std::borrow::Cow;
#[derive(Debug, Eq, PartialEq, Clone, Default, Serialize, Deserialize)]
pub struct Header<T = ()> {
#[serde(flatten)]
pub registered: RegisteredHeader,
#[serde(flatten)]
pub private: T,
}
impl<T: DeserializeOwned> FromCompactPart for Header<T> {
fn from_bytes(b: &[u8]) -> Result<Self, Error> {
Ok(serde_json::from_slice(b)?)
}
}
impl<T: Serialize> ToCompactPart for Header<T> {
fn to_bytes(&self) -> Result<Cow<'_, [u8]>, Error> {
Ok(serde_json::to_vec(&self)?.into())
}
}
impl Header<()> {
pub fn from_registered_header(registered: RegisteredHeader) -> Self {
Self {
registered,
..Default::default()
}
}
}
impl From<RegisteredHeader> for Header<()> {
fn from(registered: RegisteredHeader) -> Self {
Self::from_registered_header(registered)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RegisteredHeader {
#[serde(rename = "alg")]
pub algorithm: sign::Algorithm,
#[serde(rename = "typ", skip_serializing_if = "Option::is_none")]
pub media_type: Option<String>,
#[serde(rename = "cty", skip_serializing_if = "Option::is_none")]
pub content_type: Option<String>,
#[serde(rename = "jku", skip_serializing_if = "Option::is_none")]
pub web_key_url: Option<String>,
#[serde(rename = "jwk", skip_serializing_if = "Option::is_none")]
pub web_key: Option<jwk::JWK<()>>,
#[serde(rename = "kid", skip_serializing_if = "Option::is_none")]
pub key_id: Option<String>,
#[serde(rename = "x5u", skip_serializing_if = "Option::is_none")]
pub x509_url: Option<String>,
#[serde(rename = "x5c", skip_serializing_if = "Option::is_none")]
pub x509_chain: Option<Vec<String>>,
#[serde(rename = "x5t", skip_serializing_if = "Option::is_none")]
pub x509_fingerprint: Option<String>,
#[serde(rename = "crit", skip_serializing_if = "Option::is_none")]
pub critical: Option<Vec<String>>,
}
impl Default for RegisteredHeader {
fn default() -> RegisteredHeader {
RegisteredHeader {
algorithm: sign::Algorithm::default(),
media_type: Some("JWT".to_string()),
content_type: None,
web_key_url: None,
web_key: None,
key_id: None,
x509_url: None,
x509_chain: None,
x509_fingerprint: None,
critical: None,
}
}
}
#[cfg(test)]
mod tests {
use super::RegisteredHeader;
#[test]
fn header_serialization_round_trip_no_optional() {
let expected = RegisteredHeader::default();
let expected_json = r#"{"alg":"HS256","typ":"JWT"}"#;
let encoded = serde_json::to_string(&expected).unwrap();
assert_eq!(expected_json, encoded);
let decoded: RegisteredHeader = serde_json::from_str(&encoded).unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn header_serialization_round_trip_with_optional() {
let expected = RegisteredHeader {
key_id: Some("kid".to_string()),
..Default::default()
};
let expected_json = r#"{"alg":"HS256","typ":"JWT","kid":"kid"}"#;
let encoded = serde_json::to_string(&expected).unwrap();
assert_eq!(expected_json, encoded);
let decoded: RegisteredHeader = serde_json::from_str(&encoded).unwrap();
assert_eq!(decoded, expected);
}
}