jwt/
header.rs

1//! Convenience structs for commonly defined fields in headers.
2
3use std::borrow::Cow;
4
5use serde::{Deserialize, Serialize};
6
7use crate::algorithm::AlgorithmType;
8use crate::error::Error;
9use crate::ToBase64;
10
11/// A trait for any header than can conform to the
12/// [JWT specification](https://tools.ietf.org/html/rfc7519#page-11).
13pub trait JoseHeader {
14    fn algorithm_type(&self) -> AlgorithmType;
15
16    fn key_id(&self) -> Option<&str> {
17        None
18    }
19
20    fn type_(&self) -> Option<HeaderType> {
21        None
22    }
23
24    fn content_type(&self) -> Option<HeaderContentType> {
25        None
26    }
27}
28
29/// Generic [JWT header](https://tools.ietf.org/html/rfc7519#page-11) with
30/// defined fields for common fields.
31#[derive(Default, Debug, PartialEq, Serialize, Deserialize)]
32pub struct Header {
33    #[serde(rename = "alg")]
34    pub algorithm: AlgorithmType,
35
36    #[serde(rename = "kid", skip_serializing_if = "Option::is_none")]
37    pub key_id: Option<String>,
38
39    #[serde(rename = "typ", skip_serializing_if = "Option::is_none")]
40    pub type_: Option<HeaderType>,
41
42    #[serde(rename = "cty", skip_serializing_if = "Option::is_none")]
43    pub content_type: Option<HeaderContentType>,
44}
45
46impl JoseHeader for Header {
47    fn algorithm_type(&self) -> AlgorithmType {
48        self.algorithm
49    }
50
51    fn key_id(&self) -> Option<&str> {
52        self.key_id.as_deref()
53    }
54
55    fn type_(&self) -> Option<HeaderType> {
56        self.type_
57    }
58
59    fn content_type(&self) -> Option<HeaderContentType> {
60        self.content_type
61    }
62}
63
64#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
65#[serde(rename_all = "UPPERCASE")]
66pub enum HeaderType {
67    #[serde(rename = "JWT")]
68    JsonWebToken,
69}
70
71#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
72pub enum HeaderContentType {
73    #[serde(rename = "JWT")]
74    JsonWebToken,
75}
76
77/// A header that only contains the algorithm type. The `ToBase64`
78/// implementation uses static strings for faster serialization.
79pub struct PrecomputedAlgorithmOnlyHeader(pub AlgorithmType);
80
81impl JoseHeader for PrecomputedAlgorithmOnlyHeader {
82    fn algorithm_type(&self) -> AlgorithmType {
83        let PrecomputedAlgorithmOnlyHeader(algorithm_type) = *self;
84        algorithm_type
85    }
86}
87
88impl ToBase64 for PrecomputedAlgorithmOnlyHeader {
89    fn to_base64(&self) -> Result<Cow<'static, str>, Error> {
90        let precomputed_str = match self.algorithm_type() {
91            AlgorithmType::Hs256 => "eyJhbGciOiAiSFMyNTYifQ",
92            AlgorithmType::Hs384 => "eyJhbGciOiAiSFMzODQifQ",
93            AlgorithmType::Hs512 => "eyJhbGciOiAiSFM1MTIifQ",
94            AlgorithmType::Rs256 => "eyJhbGciOiAiUlMyNTYifQ",
95            AlgorithmType::Rs384 => "eyJhbGciOiAiUlMzODQifQ",
96            AlgorithmType::Rs512 => "eyJhbGciOiAiUlM1MTIifQ",
97            AlgorithmType::Es256 => "eyJhbGciOiAiRVMyNTYifQ",
98            AlgorithmType::Es384 => "eyJhbGciOiAiRVMzODQifQ",
99            AlgorithmType::Es512 => "eyJhbGciOiAiRVM1MTIifQ",
100            AlgorithmType::Ps256 => "eyJhbGciOiAiUFMyNTYifQ",
101            AlgorithmType::Ps384 => "eyJhbGciOiAiUFMzODQifQ",
102            AlgorithmType::Ps512 => "eyJhbGciOiAiUFM1MTIifQ",
103            AlgorithmType::None => "eyJhbGciOiAibm9uZSJ9Cg",
104        };
105
106        Ok(Cow::Borrowed(precomputed_str))
107    }
108}
109
110/// A header with a borrowed key. Used for signing claims with a Store
111/// conveniently.
112#[derive(Serialize)]
113pub(crate) struct BorrowedKeyHeader<'a> {
114    #[serde(rename = "alg")]
115    pub algorithm: AlgorithmType,
116
117    #[serde(rename = "kid")]
118    pub key_id: &'a str,
119}
120
121impl<'a> JoseHeader for BorrowedKeyHeader<'a> {
122    fn algorithm_type(&self) -> AlgorithmType {
123        self.algorithm
124    }
125
126    fn key_id(&self) -> Option<&str> {
127        Some(self.key_id)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use crate::algorithm::AlgorithmType;
134    use crate::error::Error;
135    use crate::header::{Header, HeaderType, PrecomputedAlgorithmOnlyHeader};
136    use crate::{FromBase64, ToBase64};
137
138    #[test]
139    fn from_base64() -> Result<(), Error> {
140        let enc = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
141        let header = Header::from_base64(enc)?;
142
143        assert_eq!(header.type_.unwrap(), HeaderType::JsonWebToken);
144        assert_eq!(header.algorithm, AlgorithmType::Hs256);
145
146        let enc = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFLU0YzZyJ9";
147        let header = Header::from_base64(enc)?;
148
149        assert_eq!(header.key_id.unwrap(), "1KSF3g".to_string());
150        assert_eq!(header.algorithm, AlgorithmType::Rs256);
151
152        Ok(())
153    }
154
155    #[test]
156    fn roundtrip() -> Result<(), Error> {
157        let header: Header = Default::default();
158        let enc = header.to_base64()?;
159        assert_eq!(header, Header::from_base64(&*enc)?);
160        Ok(())
161    }
162
163    #[test]
164    fn precomputed_headers() -> Result<(), Error> {
165        let algorithms = [
166            AlgorithmType::Hs256,
167            AlgorithmType::Hs384,
168            AlgorithmType::Hs512,
169            AlgorithmType::Rs256,
170            AlgorithmType::Rs384,
171            AlgorithmType::Rs512,
172            AlgorithmType::Es256,
173            AlgorithmType::Es384,
174            AlgorithmType::Es512,
175            AlgorithmType::Ps256,
176            AlgorithmType::Ps384,
177            AlgorithmType::Ps512,
178            AlgorithmType::None,
179        ];
180
181        for algorithm in algorithms.iter() {
182            let precomputed = PrecomputedAlgorithmOnlyHeader(*algorithm);
183            let precomputed_str = precomputed.to_base64()?;
184
185            let header = Header::from_base64(&*precomputed_str)?;
186            assert_eq!(*algorithm, header.algorithm);
187        }
188
189        Ok(())
190    }
191}