Saving changes
This commit is contained in:
Generated
+11
-4
@@ -123,6 +123,12 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
@@ -780,6 +786,7 @@ name = "icarus"
|
||||
version = "0.1.100"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64 0.21.7",
|
||||
"common-multipart-rfc7578",
|
||||
"futures",
|
||||
"icarus_envy",
|
||||
@@ -961,7 +968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"flate2",
|
||||
"openssl",
|
||||
"regex",
|
||||
@@ -1792,7 +1799,7 @@ version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
@@ -1868,7 +1875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@@ -1912,7 +1919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"crc",
|
||||
|
||||
@@ -19,6 +19,7 @@ uuid = { version = "1.17.0", features = ["v4", "serde"] }
|
||||
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-native-tls", "time", "uuid"] }
|
||||
time = { version = "0.3.41", features = ["formatting", "macros", "parsing", "serde"] }
|
||||
thiserror = "1.0"
|
||||
base64 = "0.21"
|
||||
josekit = { version = "0.10.1" }
|
||||
icarus_meta = { git = "ssh://git@git.kundeng.us/phoenix/icarus_meta.git", tag = "v0.3.0-devel-f4b71de969-680" }
|
||||
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.1-devel-1c5de9dc26-111" }
|
||||
|
||||
+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