rusty_common/auth/
jwt.rs

1//! JWT utilities for authentication
2
3use crate::{CommonError, Result, SmartString};
4use jsonwebtoken::{Algorithm, EncodingKey, Header};
5use serde::{Deserialize, Serialize};
6
7/// JWT Claims structure
8#[derive(Debug, Serialize, Deserialize)]
9pub struct JwtClaims {
10    /// The access key for the API.
11    pub access_key: SmartString,
12    /// A unique nonce for the request.
13    pub nonce: SmartString,
14    /// The hash of the query string, if applicable.
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub query_hash: Option<SmartString>,
17    /// The hash of the request body, if applicable.
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub body_hash: Option<SmartString>,
20    /// The expiration time for the token.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub exp: Option<i64>,
23}
24
25impl JwtClaims {
26    /// Create new JWT claims with access key
27    #[must_use]
28    pub fn new(access_key: SmartString) -> Self {
29        Self {
30            access_key,
31            nonce: uuid::Uuid::new_v4().to_string().into(),
32            query_hash: None,
33            body_hash: None,
34            exp: None,
35        }
36    }
37
38    /// Set query hash
39    #[must_use]
40    pub fn with_query_hash(mut self, query_hash: SmartString) -> Self {
41        self.query_hash = Some(query_hash);
42        self
43    }
44
45    /// Set body hash
46    #[must_use]
47    pub fn with_body_hash(mut self, body_hash: SmartString) -> Self {
48        self.body_hash = Some(body_hash);
49        self
50    }
51}
52
53/// Generate JWT token with HS256 algorithm
54pub fn generate_jwt_hs256(claims: &JwtClaims, secret: &str) -> Result<SmartString> {
55    let header = Header::new(Algorithm::HS256);
56    let encoding_key = EncodingKey::from_secret(secret.as_ref());
57
58    jsonwebtoken::encode(&header, claims, &encoding_key)
59        .map(|s| s.into())
60        .map_err(|e| CommonError::Auth(format!("JWT encoding error: {e}").into()))
61}