jwt/token/
signed.rs

1use crate::algorithm::store::Store;
2use crate::algorithm::SigningAlgorithm;
3use crate::error::Error;
4use crate::header::{BorrowedKeyHeader, Header, JoseHeader};
5use crate::token::{Signed, Unsigned};
6use crate::{ToBase64, Token, SEPARATOR};
7
8/// Allow objects to be signed with a key.
9pub trait SignWithKey<T> {
10    fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<T, Error>;
11}
12
13/// Allow objects to be signed with a store.
14pub trait SignWithStore<T> {
15    fn sign_with_store<S, A>(self, store: &S) -> Result<T, Error>
16    where
17        S: Store<Algorithm = A>,
18        A: SigningAlgorithm;
19}
20
21impl<H, C> Token<H, C, Unsigned> {
22    /// Create a new unsigned token, with mutable headers and claims.
23    pub fn new(header: H, claims: C) -> Self {
24        Token {
25            header,
26            claims,
27            signature: Unsigned,
28        }
29    }
30
31    pub fn header_mut(&mut self) -> &mut H {
32        &mut self.header
33    }
34
35    pub fn claims_mut(&mut self) -> &mut C {
36        &mut self.claims
37    }
38}
39
40impl<H, C> Default for Token<H, C, Unsigned>
41where
42    H: Default,
43    C: Default,
44{
45    fn default() -> Self {
46        Token::new(H::default(), C::default())
47    }
48}
49
50impl<C: ToBase64> SignWithKey<String> for C {
51    fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<String, Error> {
52        let header = Header {
53            algorithm: key.algorithm_type(),
54            ..Default::default()
55        };
56
57        let token = Token::new(header, self).sign_with_key(key)?;
58        Ok(token.signature.token_string)
59    }
60}
61
62impl<'a, C: ToBase64> SignWithStore<String> for (&'a str, C) {
63    fn sign_with_store<S, A>(self, store: &S) -> Result<String, Error>
64    where
65        S: Store<Algorithm = A>,
66        A: SigningAlgorithm,
67    {
68        let (key_id, claims) = self;
69        let key = store
70            .get(key_id)
71            .ok_or_else(|| Error::NoKeyWithKeyId(key_id.to_owned()))?;
72
73        let header = BorrowedKeyHeader {
74            algorithm: key.algorithm_type(),
75            key_id,
76        };
77
78        let token = Token::new(header, claims).sign_with_key(key)?;
79        Ok(token.signature.token_string)
80    }
81}
82
83impl<H, C> SignWithKey<Token<H, C, Signed>> for Token<H, C, Unsigned>
84where
85    H: ToBase64 + JoseHeader,
86    C: ToBase64,
87{
88    fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<Token<H, C, Signed>, Error> {
89        let header_algorithm = self.header.algorithm_type();
90        let key_algorithm = key.algorithm_type();
91        if header_algorithm != key_algorithm {
92            return Err(Error::AlgorithmMismatch(header_algorithm, key_algorithm));
93        }
94
95        let header = self.header.to_base64()?;
96        let claims = self.claims.to_base64()?;
97        let signature = key.sign(&header, &claims)?;
98
99        let token_string = [&*header, &*claims, &signature].join(SEPARATOR);
100
101        Ok(Token {
102            header: self.header,
103            claims: self.claims,
104            signature: Signed { token_string },
105        })
106    }
107}
108
109impl<H, C> SignWithStore<Token<H, C, Signed>> for Token<H, C, Unsigned>
110where
111    H: ToBase64 + JoseHeader,
112    C: ToBase64,
113{
114    fn sign_with_store<S, A>(self, store: &S) -> Result<Token<H, C, Signed>, Error>
115    where
116        S: Store<Algorithm = A>,
117        A: SigningAlgorithm,
118    {
119        let key_id = self.header.key_id().ok_or(Error::NoKeyId)?;
120        let key = store
121            .get(key_id)
122            .ok_or_else(|| Error::NoKeyWithKeyId(key_id.to_owned()))?;
123        self.sign_with_key(key)
124    }
125}
126
127impl<'a, H, C> Token<H, C, Signed> {
128    /// Get the string representation of the token.
129    pub fn as_str(&self) -> &str {
130        &self.signature.token_string
131    }
132}
133
134impl<H, C> From<Token<H, C, Signed>> for String {
135    fn from(token: Token<H, C, Signed>) -> Self {
136        token.signature.token_string
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use std::collections::BTreeMap;
143
144    use hmac::{Hmac, Mac};
145    use serde::Serialize;
146    use sha2::{Sha256, Sha512};
147
148    use crate::algorithm::AlgorithmType;
149    use crate::error::Error;
150    use crate::header::Header;
151    use crate::token::signed::{SignWithKey, SignWithStore};
152    use crate::Token;
153
154    #[derive(Serialize)]
155    struct Claims<'a> {
156        name: &'a str,
157    }
158
159    #[test]
160    pub fn sign_claims() -> Result<(), Error> {
161        let claims = Claims { name: "John Doe" };
162        let key: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
163
164        let signed_token = claims.sign_with_key(&key)?;
165
166        assert_eq!(signed_token, "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiBEb2UifQ.LlTGHPZRXbci-y349jXXN0byQniQQqwKGybzQCFIgY0");
167        Ok(())
168    }
169
170    #[test]
171    pub fn sign_unsigned_with_store() -> Result<(), Error> {
172        let mut key_store = BTreeMap::new();
173        let key1: Hmac<Sha512> = Hmac::new_from_slice(b"first")?;
174        let key2: Hmac<Sha512> = Hmac::new_from_slice(b"second")?;
175        key_store.insert("first_key".to_owned(), key1);
176        key_store.insert("second_key".to_owned(), key2);
177
178        let header = Header {
179            algorithm: AlgorithmType::Hs512,
180            key_id: Some(String::from("second_key")),
181            ..Default::default()
182        };
183        let claims = Claims { name: "Jane Doe" };
184        let token = Token::new(header, claims);
185        let signed_token = token.sign_with_store(&key_store)?;
186
187        assert_eq!(signed_token.as_str(), "eyJhbGciOiJIUzUxMiIsImtpZCI6InNlY29uZF9rZXkifQ.eyJuYW1lIjoiSmFuZSBEb2UifQ.t2ON5s8DDb2hefBIWAe0jaEcp-T7b2Wevmj0kKJ8BFxKNQURHpdh4IA-wbmBmqtiCnqTGoRdqK45hhW0AOtz0A");
188        Ok(())
189    }
190}