Skip to main content

json_web_token/
lib.rs

1use basekit::base64::{encode_v1, Base64EncodeConfig, ALPHABET_BASE64_URL, PADDING_BASE64};
2use bytes::Bytes;
3use lazy_static::lazy_static;
4
5pub mod header;
6pub mod payload;
7pub mod signer;
8
9use header::Header;
10use payload::Payload;
11use signer::Signer;
12
13lazy_static! {
14    static ref B64_CONFIG: Base64EncodeConfig =
15        Base64EncodeConfig::new(&ALPHABET_BASE64_URL, PADDING_BASE64);
16}
17
18#[derive(Debug)]
19pub struct Jwt {
20    pub bytes: Bytes,
21    pub header: Header,
22    pub header_bytes: Bytes,
23    pub payload: Payload,
24    pub payload_bytes: Bytes,
25    pub signature: Vec<u8>,
26    pub signature_bytes: Bytes,
27}
28
29#[derive(Debug, Clone)]
30pub struct JwtBuilder {
31    pub payload: Payload,
32}
33
34impl JwtBuilder {
35    pub fn build(self, signer: &Signer) -> Jwt {
36        let signer = signer.clone();
37
38        let header = signer.header();
39        let header_json = serde_json::to_string(&header).unwrap();
40        let payload_json = serde_json::to_string(&self.payload).unwrap();
41
42        let header_encoded = encode_v1(&B64_CONFIG, header_json.as_bytes());
43        let payload_encoded = encode_v1(&B64_CONFIG, payload_json.as_bytes());
44
45        let header_encoded_str = String::from_utf8(header_encoded)
46            .unwrap()
47            .trim_end_matches('=')
48            .to_string();
49        let payload_encoded_str = String::from_utf8(payload_encoded)
50            .unwrap()
51            .trim_end_matches('=')
52            .to_string();
53
54        let header_num_bytes = header_encoded_str.len();
55        let payload_num_bytes = payload_encoded_str.len();
56
57        let mut signing_input = header_encoded_str.clone();
58        signing_input.push('.');
59        signing_input.push_str(&payload_encoded_str);
60
61        let signature = signer.signature(signing_input.as_bytes());
62        let signature_encoded = encode_v1(&B64_CONFIG, &signature);
63        let signature_encoded_str = String::from_utf8(signature_encoded)
64            .unwrap()
65            .trim_end_matches('=')
66            .to_string();
67        let signature_num_bytes = signature_encoded_str.len();
68
69        let mut jwt_string = header_encoded_str;
70        jwt_string.push('.');
71        jwt_string.push_str(&payload_encoded_str);
72        jwt_string.push('.');
73        jwt_string.push_str(&signature_encoded_str);
74
75        let bytes = Bytes::from(jwt_string.into_bytes());
76
77        let header_bytes = bytes.slice(0..header_num_bytes);
78        let payload_bytes =
79            bytes.slice((header_num_bytes + 1)..(header_num_bytes + payload_num_bytes + 1));
80        let signature_bytes = bytes.slice(
81            (header_num_bytes + payload_num_bytes + 2)
82                ..(header_num_bytes + payload_num_bytes + signature_num_bytes + 2),
83        );
84
85        Jwt {
86            bytes,
87            header,
88            header_bytes,
89            payload: self.payload,
90            payload_bytes,
91            signature,
92            signature_bytes,
93        }
94    }
95}
96
97#[cfg(test)]
98mod jwt_builder_tests {
99    use super::*;
100    use bytes::Bytes;
101    use hmac::{Hmac, Mac};
102    use sha2::Sha256;
103
104    #[test]
105    fn new() {
106        let mut payload = Payload::new();
107        payload.issuer = Some(String::from("hello").into());
108        payload.subject = Some(true.into());
109
110        let jwt_builder = JwtBuilder { payload };
111
112        let signer =
113            Signer::HmacSha256(Hmac::<Sha256>::new_from_slice(b"your-256-bit-secret").unwrap());
114
115        let jwt = jwt_builder.build(&signer);
116
117        assert_eq!(jwt.bytes, Bytes::from("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJoZWxsbyIsInN1YiI6dHJ1ZX0.Xk_NUrp8IZ4mvrATTB67AlpBmWDTufz6JHFpz_13KZg"));
118    }
119}