Skip to main content

shared/utils/crypto/
jwt.rs

1use base64::Engine;
2use base64::prelude::BASE64_URL_SAFE_NO_PAD;
3use jsonwebtoken::errors::ErrorKind;
4use jsonwebtoken::{DecodingKey, EncodingKey};
5use serde::de::DeserializeOwned;
6
7use crate::application::model::Claims;
8use crate::config::JwtConfig;
9use crate::domain::model::SigningKey;
10use crate::error::{AuthError, CoreError, Result, TokenErrorType};
11
12#[derive(Clone)]
13pub struct JwtSigner {
14    jwt_config: JwtConfig,
15    private: String,
16    pub signing_key: SigningKey,
17}
18
19impl JwtSigner {
20    pub fn new(private: &str, signing_key: &SigningKey, jwt_config: &JwtConfig) -> Self {
21        Self {
22            jwt_config: jwt_config.clone(),
23            private: private.into(),
24            signing_key: signing_key.clone(),
25        }
26    }
27}
28
29impl JwtSigner {
30    fn load_keys(&self) -> (Result<EncodingKey>, Result<DecodingKey>) {
31        let prv_key = BASE64_URL_SAFE_NO_PAD.decode(&self.private).unwrap();
32        let pub_key = BASE64_URL_SAFE_NO_PAD
33            .decode(&self.signing_key.public_key)
34            .unwrap();
35
36        match self.signing_key.algorithm.as_str() {
37            "EdDSA" => (
38                EncodingKey::from_ed_pem(&prv_key).map_err(Into::into),
39                DecodingKey::from_ed_pem(&pub_key).map_err(Into::into),
40            ),
41            "ES256" | "ES384" => (
42                EncodingKey::from_ec_pem(&prv_key).map_err(Into::into),
43                DecodingKey::from_ec_pem(&pub_key).map_err(Into::into),
44            ),
45            "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" => (
46                EncodingKey::from_rsa_pem(&prv_key).map_err(Into::into),
47                DecodingKey::from_rsa_pem(&pub_key).map_err(Into::into),
48            ),
49            &_ => (
50                EncodingKey::from_rsa_pem(&prv_key).map_err(Into::into),
51                DecodingKey::from_rsa_pem(&pub_key).map_err(Into::into),
52            ),
53        }
54    }
55
56    pub fn encode(&self, claims: Claims) -> Result<String> {
57        let mut header = jsonwebtoken::Header::new(self.jwt_config.algorithm.clone().into());
58        header.kid = Some(self.signing_key.kid.clone());
59
60        // If your key is in PEM format, it is better performance wise to generate
61        // the EncodingKey once in a lazy_static or something similar and reuse it.
62        let encoding_secret = self.load_keys().0?;
63
64        let token = jsonwebtoken::encode(&header, &claims, &encoding_secret)?;
65        Ok(token)
66    }
67
68    pub fn decode<C: DeserializeOwned>(&self, token: &str) -> Result<C> {
69        let mut validation =
70            jsonwebtoken::Validation::new(self.jwt_config.algorithm.clone().into());
71        validation.set_audience(&[self.jwt_config.clone().audience]);
72        validation.set_issuer(&[self.jwt_config.clone().issuer]);
73
74        let decoding_key = self.load_keys().1?;
75        jsonwebtoken::decode::<C>(&token, &decoding_key, &validation)
76            .map(|token_data| token_data.claims)
77            .map_err(|e| match e.kind() {
78                ErrorKind::InvalidSignature => {
79                    CoreError::Unauthenticated(AuthError::TokenInvalidSignature {
80                        token_type: TokenErrorType::Token,
81                    })
82                }
83                ErrorKind::ExpiredSignature => {
84                    CoreError::Unauthenticated(AuthError::TokenExpired {
85                        token_type: TokenErrorType::Token,
86                        expired_at: chrono::Utc::now(),
87                    })
88                }
89                ErrorKind::InvalidAudience => {
90                    CoreError::Unauthenticated(AuthError::TokenInvalidAudience {
91                        token_type: TokenErrorType::Token,
92                    })
93                }
94                ErrorKind::InvalidIssuer => {
95                    CoreError::Unauthenticated(AuthError::TokenInvalidIssuer {
96                        token_type: TokenErrorType::Token,
97                    })
98                }
99                ErrorKind::InvalidAlgorithm | ErrorKind::MissingAlgorithm => {
100                    CoreError::Unauthenticated(AuthError::TokenInvalidAlgorithm {
101                        token_type: TokenErrorType::Token,
102                    })
103                }
104                _ => CoreError::Unauthenticated(AuthError::TokenInvalid {
105                    token_type: TokenErrorType::Token,
106                }),
107            })
108    }
109}