Files
k-photos/crates/adapters/auth/src/jwt.rs

50 lines
1.7 KiB
Rust

use async_trait::async_trait;
use chrono::Utc;
use domain::{errors::DomainError, ports::TokenIssuer, value_objects::SystemId};
use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation, decode, encode};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub role: String,
pub exp: i64,
}
pub struct JwtTokenIssuer {
encoding_key: EncodingKey,
decoding_key: DecodingKey,
expiry_hours: i64,
}
impl JwtTokenIssuer {
pub fn new(secret: &str) -> Self {
Self {
encoding_key: EncodingKey::from_secret(secret.as_bytes()),
decoding_key: DecodingKey::from_secret(secret.as_bytes()),
expiry_hours: 24,
}
}
}
#[async_trait]
impl TokenIssuer for JwtTokenIssuer {
async fn issue(&self, user_id: &SystemId, role: &str) -> Result<String, DomainError> {
let claims = Claims {
sub: user_id.to_string(),
role: role.to_string(),
exp: (Utc::now() + chrono::Duration::hours(self.expiry_hours)).timestamp(),
};
encode(&Header::default(), &claims, &self.encoding_key)
.map_err(|e| DomainError::Internal(e.to_string()))
}
async fn verify(&self, token: &str) -> Result<(SystemId, String), DomainError> {
let data = decode::<Claims>(token, &self.decoding_key, &Validation::default())
.map_err(|_| DomainError::Unauthorized("Invalid or expired token".to_string()))?;
let uuid = uuid::Uuid::parse_str(&data.claims.sub)
.map_err(|_| DomainError::Unauthorized("Invalid token subject".to_string()))?;
Ok((SystemId::from_uuid(uuid), data.claims.role))
}
}