This commit is contained in:
Brandon Thomas
2026-04-06 18:40:27 -04:00
parent 6f1045e58a
commit 0eb4ceed70
8 changed files with 61 additions and 59 deletions
Generated
-4
View File
@@ -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",
-3
View File
@@ -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]
@@ -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<Sha256>,
jwt_signer: JwtSigner,
}
impl HttpUserSessionPayloadSigner {
pub fn new(hmac_secret: &str) -> Result<Self, SessionError> {
let hmac_key = Hmac::<Sha256>::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<HttpUserSessionPayload, SessionError> {
let claims: BTreeMap<String, String> = 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")
@@ -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<JwtSignerError> for SessionError {
fn from(err: JwtSignerError) -> Self {
Self::JwtSigner(err)
}
}
-3
View File
@@ -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"
+16 -17
View File
@@ -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<Self> {
pub fn new(hmac_secret: &str) -> Result<Self, JwtSignerError> {
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<String> {
let jwt_string = claims.sign(&self.hmac_key)?;
Ok(jwt_string)
pub fn claims_to_jwt(&self, claims: &impl Claimable) -> Result<String, JwtSignerError> {
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<BTreeMap<String, String>> {
let claims = jwt.verify_with_key(&self.hmac_key)?;
Ok(claims)
pub fn jwt_to_claims(&self, jwt: &str) -> Result<BTreeMap<String, String>, JwtSignerError> {
jwt.verify_with_key(&self.hmac_key)
.map_err(JwtSignerError::JwtVerifyError)
}
}
pub trait Claimable {
fn sign(&self, hmac_key: &Hmac<Sha256>) -> AnyhowResult<String>;
fn sign(&self, hmac_key: &Hmac<Sha256>) -> Result<String, JwtSignerError>;
}
impl Claimable for BTreeMap<String, String> {
fn sign(&self, hmac_key: &Hmac<Sha256>) -> AnyhowResult<String> {
let claims = self.sign_with_key(hmac_key)?;
Ok(claims)
fn sign(&self, hmac_key: &Hmac<Sha256>) -> Result<String, JwtSignerError> {
self.sign_with_key(hmac_key)
.map_err(JwtSignerError::JwtSignError)
}
}
impl Claimable for BTreeMap<&str, &str> {
fn sign(&self, hmac_key: &Hmac<Sha256>) -> AnyhowResult<String> {
let claims = self.sign_with_key(hmac_key)?;
Ok(claims)
fn sign(&self, hmac_key: &Hmac<Sha256>) -> Result<String, JwtSignerError> {
self.sign_with_key(hmac_key)
.map_err(JwtSignerError::JwtSignError)
}
}
@@ -92,4 +91,4 @@ mod tests {
assert_eq!("eyJhbGciOiJIUzI1NiJ9.eyJiYXIiOiJubyIsImZvbyI6InllcyJ9.k4eRvIrtw4tuGDtQqSmEDngtGYAaQVRYVMCK5N3JcZA", &jwt);
}
}
}
@@ -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 {}
+2 -1
View File
@@ -18,4 +18,5 @@
#![allow(dead_code)]
#![allow(non_snake_case)]
pub mod jwt_signer;
pub mod jwt_signer;
pub mod jwt_signer_error;