1use base64ct::{Base64UrlUnpadded, Encoding};
2use ring::hmac::{self, HMAC_SHA256};
3use serde::{de::DeserializeOwned, ser::Serialize};
4
5pub use serde_json::Error as JsonError;
7
8#[derive(Debug)]
9pub enum DecodeError {
10 InvalidToken,
11 InvalidSignature,
12 Json(JsonError),
13}
14
15impl From<JsonError> for DecodeError {
16 fn from(value: JsonError) -> Self {
17 Self::Json(value)
18 }
19}
20
21impl From<base64ct::Error> for DecodeError {
22 fn from(_: base64ct::Error) -> Self {
23 Self::InvalidToken
24 }
25}
26
27pub struct Tokens {
29 key: hmac::Key,
30}
31
32impl Tokens {
33 pub fn new(secret: &[u8]) -> Self {
38 let key = hmac::Key::new(HMAC_SHA256, secret);
40 Self { key }
41 }
42
43 pub fn encode<T: Serialize>(&self, claims: &T) -> Result<String, JsonError> {
48 let msg_bytes = serde_json::to_vec(claims)?;
50 let msg = Base64UrlUnpadded::encode_string(&msg_bytes);
51
52 let sig = hmac::sign(&self.key, &msg_bytes);
54 let sig = Base64UrlUnpadded::encode_string(sig.as_ref());
55
56 Ok([msg, sig].join("."))
58 }
59
60 pub fn decode<T: DeserializeOwned>(&self, token: &str) -> Result<T, DecodeError> {
64 let (msg, sig) = match token.split_once('.') {
66 Some(value) => value,
67 None => return Err(DecodeError::InvalidToken),
68 };
69
70 let msg: Vec<u8> = Base64UrlUnpadded::decode_vec(msg)?;
72 let sig: Vec<u8> = Base64UrlUnpadded::decode_vec(sig)?;
73
74 if hmac::verify(&self.key, &msg, &sig).is_err() {
76 return Err(DecodeError::InvalidSignature);
77 }
78
79 let claims: T = serde_json::from_slice(&msg)?;
81 Ok(claims)
82 }
83}