hs256_token/
lib.rs

1use base64ct::{Base64UrlUnpadded, Encoding};
2use ring::hmac::{self, HMAC_SHA256};
3use serde::{de::DeserializeOwned, ser::Serialize};
4
5// Re-export for JSON error type
6pub 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
27/// Tokens interface for creating and validating tokens
28pub struct Tokens {
29    key: hmac::Key,
30}
31
32impl Tokens {
33    /// Creates a new tokens interface from the provided
34    /// secret key
35    ///
36    /// `secret` The secret key
37    pub fn new(secret: &[u8]) -> Self {
38        // Create a new HMAC key using the provided secret
39        let key = hmac::Key::new(HMAC_SHA256, secret);
40        Self { key }
41    }
42
43    /// Encodes the provided claims into a token returning the
44    /// encoded token
45    ///
46    /// `claims` The claims to encode
47    pub fn encode<T: Serialize>(&self, claims: &T) -> Result<String, JsonError> {
48        // Encode the message
49        let msg_bytes = serde_json::to_vec(claims)?;
50        let msg = Base64UrlUnpadded::encode_string(&msg_bytes);
51
52        // Create a signature from the raw message bytes
53        let sig = hmac::sign(&self.key, &msg_bytes);
54        let sig = Base64UrlUnpadded::encode_string(sig.as_ref());
55
56        // Join the message and signature to create the token
57        Ok([msg, sig].join("."))
58    }
59
60    /// Decodes a token claims from the provided token string
61    ///
62    /// `token` The token to decode
63    pub fn decode<T: DeserializeOwned>(&self, token: &str) -> Result<T, DecodeError> {
64        // Split the token parts
65        let (msg, sig) = match token.split_once('.') {
66            Some(value) => value,
67            None => return Err(DecodeError::InvalidToken),
68        };
69
70        // Decode the message signature
71        let msg: Vec<u8> = Base64UrlUnpadded::decode_vec(msg)?;
72        let sig: Vec<u8> = Base64UrlUnpadded::decode_vec(sig)?;
73
74        // Verify the signature
75        if hmac::verify(&self.key, &msg, &sig).is_err() {
76            return Err(DecodeError::InvalidSignature);
77        }
78
79        // Decode the verified token claims
80        let claims: T = serde_json::from_slice(&msg)?;
81        Ok(claims)
82    }
83}