diff --git a/Cargo.lock b/Cargo.lock index 943e4a54a3..1c2f68a8d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,12 +341,9 @@ dependencies = [ "anyhow", "enums", "errors", - "hmac 0.10.1", "http_headers", - "jwt", "jwt_signer", "log", - "sha2 0.9.9", "tokens", "workspace-build-acceleration", ] @@ -6092,7 +6089,6 @@ dependencies = [ name = "jwt_signer" version = "0.0.1" dependencies = [ - "errors", "hmac 0.10.1", "jwt", "sha2 0.9.9", diff --git a/crates/lib/actix_artcraft/Cargo.toml b/crates/lib/actix_artcraft/Cargo.toml index 502e41d0da..a1dec467af 100644 --- a/crates/lib/actix_artcraft/Cargo.toml +++ b/crates/lib/actix_artcraft/Cargo.toml @@ -32,11 +32,8 @@ tokens.workspace = true actix-http.workspace = true actix-service.workspace = true actix-web.workspace = true -hmac = "0.10.1" -jwt = "0.13.0" log.workspace = true anyhow.workspace = true -sha2 = "0.9.9" [dev-dependencies] diff --git a/crates/lib/actix_artcraft/src/sessions/payload_signer.rs b/crates/lib/actix_artcraft/src/sessions/payload_signer.rs index 2d21d17ffa..6a648e8acf 100644 --- a/crates/lib/actix_artcraft/src/sessions/payload_signer.rs +++ b/crates/lib/actix_artcraft/src/sessions/payload_signer.rs @@ -1,14 +1,8 @@ use std::collections::BTreeMap; -use hmac::Hmac; -use hmac::NewMac; -use jwt::SignWithKey; -use jwt::VerifyWithKey; -use log::error; -use sha2::Sha256; - use crate::sessions::http_user_session_payload::HttpUserSessionPayload; use crate::sessions::session_error::SessionError; +use jwt_signer::jwt_signer::JwtSigner; use tokens::tokens::user_sessions::UserSessionToken; use tokens::tokens::users::UserToken; @@ -23,19 +17,13 @@ const PAYLOAD_VERSION : u32 = 3; #[derive(Clone)] pub struct HttpUserSessionPayloadSigner { - hmac_key: Hmac, + jwt_signer: JwtSigner, } impl HttpUserSessionPayloadSigner { pub fn new(hmac_secret: &str) -> Result { - let hmac_key = Hmac::::new_varkey(hmac_secret.as_bytes()) - .map_err(|err| { - error!("Failed to construct Hmac signer: {:?}", err); - SessionError::JwtInvalidKeyLength - })?; - Ok(Self { - hmac_key, + jwt_signer: JwtSigner::new(hmac_secret)?, }) } @@ -51,17 +39,15 @@ impl HttpUserSessionPayloadSigner { claims.insert("user_token", user_token.as_str()); claims.insert("version", &payload_version); - claims.sign_with_key(&self.hmac_key) - .map_err(SessionError::JwtSignError) + let jwt_string = self.jwt_signer.claims_to_jwt(&claims)?; + Ok(jwt_string) } pub fn decode( &self, session_payload_contents: &str, ) -> Result { - let claims: BTreeMap = session_payload_contents - .verify_with_key(&self.hmac_key) - .map_err(SessionError::JwtVerifyError)?; + let claims = self.jwt_signer.jwt_to_claims(session_payload_contents)?; let session_token = claims["session_token"].clone(); let maybe_user_token = claims.get("user_token") diff --git a/crates/lib/actix_artcraft/src/sessions/session_error.rs b/crates/lib/actix_artcraft/src/sessions/session_error.rs index 1f779448e8..92cd34d0f4 100644 --- a/crates/lib/actix_artcraft/src/sessions/session_error.rs +++ b/crates/lib/actix_artcraft/src/sessions/session_error.rs @@ -1,30 +1,30 @@ use std::error::Error; use std::fmt::{Display, Formatter}; +use jwt_signer::jwt_signer_error::JwtSignerError; + #[derive(Debug)] pub enum SessionError { /// Error reading HTTP header HttpSessionHeaderError(String), - /// Error decoding and verifying a JWT to claims - JwtVerifyError(jwt::Error), - - /// Error encoding and signing JWT claims - JwtSignError(jwt::Error), - - /// Error constructing the JWT signer. - JwtInvalidKeyLength, + /// Error encoding, decoding, or constructing the JWT signer. + JwtSigner(JwtSignerError), } impl Display for SessionError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::HttpSessionHeaderError(msg) => write!(f, "HTTP session header error: {}", msg), - Self::JwtVerifyError(e) => write!(f, "JWT verify error: {}", e), - Self::JwtSignError(e) => write!(f, "JWT sign error: {}", e), - Self::JwtInvalidKeyLength => write!(f, "JWT signer construction failed: invalid key length"), + Self::JwtSigner(e) => write!(f, "JWT signer error: {}", e), } } } impl Error for SessionError {} + +impl From for SessionError { + fn from(err: JwtSignerError) -> Self { + Self::JwtSigner(err) + } +} diff --git a/crates/lib/jwt_signer/Cargo.toml b/crates/lib/jwt_signer/Cargo.toml index 2ecc02a03a..57b5d1612b 100644 --- a/crates/lib/jwt_signer/Cargo.toml +++ b/crates/lib/jwt_signer/Cargo.toml @@ -19,9 +19,6 @@ normal = ["workspace-build-acceleration"] # Workspace hack (faster compile times) workspace-build-acceleration = { version = "0.1", path = "../../workspace-build-acceleration" } -# Internal -errors.workspace = true - # External hmac = "0.10.1" # jwt signing jwt = "0.13.0" diff --git a/crates/lib/jwt_signer/src/jwt_signer.rs b/crates/lib/jwt_signer/src/jwt_signer.rs index c6bf7445ad..1b5c87e2b8 100644 --- a/crates/lib/jwt_signer/src/jwt_signer.rs +++ b/crates/lib/jwt_signer/src/jwt_signer.rs @@ -6,7 +6,7 @@ use jwt::SignWithKey; use jwt::VerifyWithKey; use sha2::Sha256; -use errors::{anyhow, AnyhowResult}; +use crate::jwt_signer_error::JwtSignerError; #[derive(Clone)] pub struct JwtSigner { @@ -15,9 +15,9 @@ pub struct JwtSigner { impl JwtSigner { - pub fn new(hmac_secret: &str) -> AnyhowResult { + pub fn new(hmac_secret: &str) -> Result { let hmac_key = Hmac::new_varkey(hmac_secret.as_bytes()) - .map_err(|e| anyhow!("invalid hmac: {:?}", e))?; + .map_err(|_| JwtSignerError::JwtInvalidKeyLength)?; Ok(Self { hmac_key, @@ -25,33 +25,32 @@ impl JwtSigner { } /// Turn a map of claims into a signed JWT cookie payload (string) - pub fn claims_to_jwt(&self, claims: &impl Claimable) -> AnyhowResult { - let jwt_string = claims.sign(&self.hmac_key)?; - Ok(jwt_string) + pub fn claims_to_jwt(&self, claims: &impl Claimable) -> Result { + claims.sign(&self.hmac_key) } /// Turn a JWT cookie payload (string) into a map of (key, value) claims - pub fn jwt_to_claims(&self, jwt: &str) -> AnyhowResult> { - let claims = jwt.verify_with_key(&self.hmac_key)?; - Ok(claims) + pub fn jwt_to_claims(&self, jwt: &str) -> Result, JwtSignerError> { + jwt.verify_with_key(&self.hmac_key) + .map_err(JwtSignerError::JwtVerifyError) } } pub trait Claimable { - fn sign(&self, hmac_key: &Hmac) -> AnyhowResult; + fn sign(&self, hmac_key: &Hmac) -> Result; } impl Claimable for BTreeMap { - fn sign(&self, hmac_key: &Hmac) -> AnyhowResult { - let claims = self.sign_with_key(hmac_key)?; - Ok(claims) + fn sign(&self, hmac_key: &Hmac) -> Result { + self.sign_with_key(hmac_key) + .map_err(JwtSignerError::JwtSignError) } } impl Claimable for BTreeMap<&str, &str> { - fn sign(&self, hmac_key: &Hmac) -> AnyhowResult { - let claims = self.sign_with_key(hmac_key)?; - Ok(claims) + fn sign(&self, hmac_key: &Hmac) -> Result { + self.sign_with_key(hmac_key) + .map_err(JwtSignerError::JwtSignError) } } @@ -92,4 +91,4 @@ mod tests { assert_eq!("eyJhbGciOiJIUzI1NiJ9.eyJiYXIiOiJubyIsImZvbyI6InllcyJ9.k4eRvIrtw4tuGDtQqSmEDngtGYAaQVRYVMCK5N3JcZA", &jwt); } -} \ No newline at end of file +} diff --git a/crates/lib/jwt_signer/src/jwt_signer_error.rs b/crates/lib/jwt_signer/src/jwt_signer_error.rs new file mode 100644 index 0000000000..d6180496c7 --- /dev/null +++ b/crates/lib/jwt_signer/src/jwt_signer_error.rs @@ -0,0 +1,26 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum JwtSignerError { + /// Error decoding and verifying a JWT to claims + JwtVerifyError(jwt::Error), + + /// Error encoding and signing JWT claims + JwtSignError(jwt::Error), + + /// Error constructing the JWT signer. + JwtInvalidKeyLength, +} + +impl Display for JwtSignerError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::JwtVerifyError(e) => write!(f, "JWT verify error: {}", e), + Self::JwtSignError(e) => write!(f, "JWT sign error: {}", e), + Self::JwtInvalidKeyLength => write!(f, "JWT signer construction failed: invalid key length"), + } + } +} + +impl Error for JwtSignerError {} diff --git a/crates/lib/jwt_signer/src/lib.rs b/crates/lib/jwt_signer/src/lib.rs index c2c14cd1fb..6e82ed4189 100644 --- a/crates/lib/jwt_signer/src/lib.rs +++ b/crates/lib/jwt_signer/src/lib.rs @@ -18,4 +18,5 @@ #![allow(dead_code)] #![allow(non_snake_case)] -pub mod jwt_signer; \ No newline at end of file +pub mod jwt_signer; +pub mod jwt_signer_error; \ No newline at end of file