Saving changes
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
|
||||
use axum::{
|
||||
http::{Request, StatusCode},
|
||||
middleware::Next,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use josekit::{
|
||||
jwt::{self, JwtPayload},
|
||||
jws::{self, JwsHeader, JwsSigner, JwsVerifier},
|
||||
jwk::Jwk,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use thiserror::Error;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UserClaims {
|
||||
pub sub: String, // Subject (user ID)
|
||||
pub exp: i64, // Expiration time (UTC timestamp)
|
||||
pub iat: i64, // Issued at (UTC timestamp)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub roles: Option<Vec<String>>, // Optional roles
|
||||
}
|
||||
|
||||
|
||||
// use time::OffsetDateTime;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum JwtError {
|
||||
#[error("Token creation failed")]
|
||||
TokenCreation,
|
||||
#[error("Token verification failed")]
|
||||
TokenVerification,
|
||||
#[error("Invalid token")]
|
||||
InvalidToken,
|
||||
#[error("Token expired")]
|
||||
ExpiredToken,
|
||||
#[error("Invalid key")]
|
||||
InvalidKey,
|
||||
}
|
||||
|
||||
pub struct JwtAuth {
|
||||
key: Jwk, // Now properly parameterized
|
||||
}
|
||||
|
||||
impl JwtAuth {
|
||||
pub fn new(secret: &str) -> Result<Self, JwtError> {
|
||||
let mut key = Jwk::new("oct");
|
||||
key.set_parameter("k", Some(json!(base64::encode(secret))))
|
||||
.map_err(|_| JwtError::InvalidKey)?;
|
||||
Ok(Self { key })
|
||||
}
|
||||
|
||||
pub fn create_token(&self, claims: &UserClaims) -> Result<String, JwtError> {
|
||||
let mut header = JwsHeader::new();
|
||||
header.set_token_type("JWT");
|
||||
|
||||
let claims_value = serde_json::to_value(claims)
|
||||
.map_err(|_| JwtError::SerializationError)?;
|
||||
let claims_map = claims_value.as_object()
|
||||
.ok_or(JwtError::SerializationError)?
|
||||
.to_owned();
|
||||
|
||||
let mut payload = JwtPayload::new();
|
||||
for (k, v) in claims_map {
|
||||
payload.set_claim(&k, Some(v))
|
||||
.map_err(|_| JwtError::TokenCreation)?;
|
||||
}
|
||||
|
||||
let signer = jws::HS256.signer_from_jwk(&self.key)
|
||||
.map_err(|_| JwtError::TokenCreation)?;
|
||||
|
||||
jwt::encode_with_signer(&payload, &header, &signer)
|
||||
.map_err(|_| JwtError::TokenCreation)
|
||||
}
|
||||
|
||||
pub fn verify_token(&self, token: &str) -> Result<UserClaims, JwtError> {
|
||||
let verifier = jws::HS256.verifier_from_jwk(&self.key)
|
||||
.map_err(|_| JwtError::TokenVerification)?;
|
||||
|
||||
let (payload, _) = jwt::decode_with_verifier(token, &verifier)
|
||||
.map_err(|_| JwtError::InvalidToken)?;
|
||||
|
||||
// Convert payload to JSON value
|
||||
let mut claims_value = json!({});
|
||||
if let Some(Value::Object(map)) = claims_value.as_object_mut() {
|
||||
for key in payload.claim_names() {
|
||||
if let Some(value) = payload.claim(key) {
|
||||
map.insert(key.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let claims = serde_json::from_value(claims_value)
|
||||
.map_err(|_| JwtError::InvalidToken)?;
|
||||
|
||||
// Check expiration
|
||||
if let Some(Value::Number(exp)) = payload.claim("exp") {
|
||||
if let Some(exp) = exp.as_i64() {
|
||||
let now = OffsetDateTime::now_utc().unix_timestamp();
|
||||
if exp < now {
|
||||
return Err(JwtError::ExpiredToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(claims)
|
||||
}
|
||||
}
|
||||
+1
-24
@@ -1,3 +1,4 @@
|
||||
pub mod auth;
|
||||
pub mod callers;
|
||||
|
||||
pub mod db {
|
||||
@@ -156,30 +157,6 @@ pub mod init {
|
||||
}
|
||||
}
|
||||
|
||||
mod auth {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use axum::{
|
||||
http::{Request, StatusCode},
|
||||
middleware::Next,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UserClaims {
|
||||
pub sub: String, // Subject (user ID)
|
||||
pub exp: i64, // Expiration time (UTC timestamp)
|
||||
pub iat: i64, // Issued at (UTC timestamp)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub roles: Option<Vec<String>>, // Optional roles
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Move elsewhere
|
||||
fn get_full() -> String {
|
||||
|
||||
Reference in New Issue
Block a user