From 775410907baf951b38c1cb0d85ab88859fe2fa52 Mon Sep 17 00:00:00 2001 From: phoenix Date: Sun, 31 May 2026 16:46:32 -0400 Subject: [PATCH] Adding token_stuff repo --- Cargo.lock | 3 +- Cargo.toml | 2 +- src/main.rs | 1 + src/token_stuff/mod.rs | 141 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 src/token_stuff/mod.rs diff --git a/Cargo.lock b/Cargo.lock index ce66fd3..9015e0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2241,10 +2241,11 @@ dependencies = [ [[package]] name = "textsender_models" version = "0.3.0" -source = "git+ssh://git@git.kundeng.us/phoenix/textsender-models.git?tag=v0.3.0-v0.3.0-migrate-1340440fec-111#1340440fecd79463bad053539c47e65d8da73194" +source = "git+ssh://git@git.kundeng.us/phoenix/textsender-models.git?tag=v0.3.0-17-f920e91ec0-111#f920e91ec07487dbe1a72207a8bd2be84c0e95b9" dependencies = [ "const_format", "dotenvy", + "josekit", "rand 0.10.1", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 63b06f8..568a863 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ time = { version = "0.3.47", features = ["macros", "serde"] } josekit = { version = "0.10.3" } utoipa = { version = "5.5.0", features = ["axum_extras"] } utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] } -textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender-models.git", tag = "v0.3.0-v0.3.0-migrate-1340440fec-111" } +textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender-models.git", tag = "v0.3.0-17-f920e91ec0-111" } # icarus_envy = { git = "ssh://git@git.kundeng.us/phoenix/icarus_envy.git", tag = "v0.7.0" } # dotenvy = { version = "0.15.7" } # const_format = { version = "0.2.36" } diff --git a/src/main.rs b/src/main.rs index ef34d8b..db58dac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ pub mod config; pub mod db; pub mod hashing; pub mod repo; +pub mod token_stuff; #[tokio::main] diff --git a/src/token_stuff/mod.rs b/src/token_stuff/mod.rs new file mode 100644 index 0000000..a453f3e --- /dev/null +++ b/src/token_stuff/mod.rs @@ -0,0 +1,141 @@ +use josekit::{ + self, + jws::alg::hmac::HmacJwsAlgorithm::Hs256, + jwt::{self}, +}; + +use time; + +pub const KEY_ENV: &str = "SECRET_KEY"; +pub const MESSAGE: &str = "Something random"; +pub const ISSUER: &str = "textsender_auth"; +pub const AUDIENCE: &str = "textsender"; + +pub fn get_issued() -> time::Result { + Ok(time::OffsetDateTime::now_utc()) +} + +pub fn get_expiration(issued: &time::OffsetDateTime) -> Result { + let duration_expire = time::Duration::hours(4); + Ok(*issued + duration_expire) +} + +pub fn create_token( + provided_key: &String, + id: &uuid::Uuid, +) -> Result<(String, i64), josekit::JoseError> { + let resource = textsender_models::token::TokenResource { + message: String::from(MESSAGE), + issuer: String::from(ISSUER), + audiences: vec![String::from(AUDIENCE)], + id: *id, + }; + textsender_models::token::create_token(provided_key, &resource, time::Duration::hours(4)) +} + +pub fn create_service_token( + provided: &String, + id: &uuid::Uuid, +) -> Result<(String, i64), josekit::JoseError> { + let resource = textsender_models::token::TokenResource { + message: String::from(SERVICE_SUBJECT), + issuer: String::from(ISSUER), + audiences: vec![String::from(AUDIENCE)], + id: *id, + }; + textsender_models::token::create_token(provided, &resource, time::Duration::hours(1)) +} + +pub fn create_service_refresh_token( + key: &String, + id: &uuid::Uuid, +) -> Result<(String, i64), josekit::JoseError> { + let resource = textsender_models::token::TokenResource { + message: String::from(SERVICE_SUBJECT), + issuer: String::from(ISSUER), + audiences: vec![String::from(AUDIENCE)], + id: *id, + }; + textsender_models::token::create_token(key, &resource, time::Duration::hours(4)) +} + +pub fn verify_token(key: &String, token: &String) -> bool { + match get_payload(key, token) { + Ok((payload, _header)) => match payload.subject() { + Some(_sub) => true, + None => false, + }, + Err(_err) => false, + } +} + +pub fn extract_id_from_token(key: &String, token: &String) -> Result { + match get_payload(key, token) { + Ok((payload, _header)) => match payload.claim("id") { + Some(id) => match uuid::Uuid::parse_str(id.as_str().unwrap()) { + Ok(extracted) => Ok(extracted), + Err(err) => Err(std::io::Error::other(err.to_string())), + }, + None => Err(std::io::Error::other("No claim found")), + }, + Err(err) => Err(std::io::Error::other(err.to_string())), + } +} + +pub const APP_TOKEN_TYPE: &str = "Icarus_App"; +pub const APP_SUBJECT: &str = "Something random"; +pub const SERVICE_TOKEN_TYPE: &str = "Icarus_Service"; +pub const SERVICE_SUBJECT: &str = "Service random"; + +pub fn get_token_type(key: &String, token: &String) -> Result { + match get_payload(key, token) { + Ok((payload, _header)) => match payload.subject() { + Some(subject) => { + if subject == APP_SUBJECT { + Ok(String::from(APP_TOKEN_TYPE)) + } else if subject == SERVICE_SUBJECT { + Ok(String::from(SERVICE_TOKEN_TYPE)) + } else { + Err(std::io::Error::other(String::from("Invalid subject"))) + } + } + None => Err(std::io::Error::other(String::from("Invalid payload"))), + }, + Err(err) => Err(std::io::Error::other(err.to_string())), + } +} + +pub fn is_token_type_valid(token_type: &String) -> bool { + token_type == SERVICE_TOKEN_TYPE +} + +fn get_payload( + key: &String, + token: &String, +) -> Result<(josekit::jwt::JwtPayload, josekit::jws::JwsHeader), josekit::JoseError> { + let ver = Hs256.verifier_from_bytes(key.as_bytes()).unwrap(); + jwt::decode_with_verifier(token, &ver) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tokenize() { + let rt = tokio::runtime::Runtime::new().unwrap(); + let special_key = rt + .block_on(textsender_models::envy::environment::get_secret_key()) + .value; + let id = uuid::Uuid::new_v4(); + match create_token(&special_key, &id) { + Ok((token, _duration)) => { + let result = verify_token(&special_key, &token); + assert!(result, "Token not verified"); + } + Err(err) => { + assert!(false, "Error: {:?}", err.to_string()); + } + }; + } +}