json_web_token/
lib.rs

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