2024-11-16 23:03:55 +08:00
|
|
|
use jwt_compact::{alg::Ed25519, AlgorithmExt, Header, Token, UntrustedToken, TimeOptions};
|
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
use chrono::{Duration, Utc};
|
|
|
|
use ed25519_dalek::{SigningKey, VerifyingKey};
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::Write;
|
|
|
|
use std::{env, fs};
|
|
|
|
use std::error::Error;
|
|
|
|
use rand::{SeedableRng, RngCore};
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
|
|
pub struct CustomClaims {
|
2024-11-19 00:20:31 +08:00
|
|
|
pub user_id: String,
|
|
|
|
pub device_ua: String,
|
2024-11-16 23:03:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum SecretKey {
|
2024-11-19 00:20:31 +08:00
|
|
|
Signing,
|
|
|
|
Verifying,
|
2024-11-16 23:03:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SecretKey {
|
|
|
|
fn as_string(&self) -> String {
|
|
|
|
match self {
|
|
|
|
Self::Signing => String::from("signing"),
|
|
|
|
Self::Verifying => String::from("verifying"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn generate_key() -> Result<(), Box<dyn Error>> {
|
2024-11-19 00:20:31 +08:00
|
|
|
let mut csprng = rand::rngs::StdRng::from_entropy();
|
2024-11-16 23:03:55 +08:00
|
|
|
|
2024-11-19 00:20:31 +08:00
|
|
|
let mut private_key_bytes = [0u8; 32];
|
|
|
|
csprng.fill_bytes(&mut private_key_bytes);
|
2024-11-16 23:03:55 +08:00
|
|
|
|
2024-11-19 00:20:31 +08:00
|
|
|
let signing_key = SigningKey::from_bytes(&private_key_bytes);
|
|
|
|
let verifying_key = signing_key.verifying_key();
|
2024-11-16 23:03:55 +08:00
|
|
|
|
|
|
|
let base_path = env::current_dir()?
|
|
|
|
.join("assets")
|
|
|
|
.join("key");
|
|
|
|
|
2024-11-19 00:20:31 +08:00
|
|
|
fs::create_dir_all(&base_path)?;
|
2024-11-16 23:03:55 +08:00
|
|
|
File::create(base_path.join(SecretKey::Signing.as_string()))?
|
2024-11-19 00:20:31 +08:00
|
|
|
.write_all(signing_key.as_bytes())?;
|
2024-11-16 23:03:55 +08:00
|
|
|
File::create(base_path.join(SecretKey::Verifying.as_string()))?
|
2024-11-19 00:20:31 +08:00
|
|
|
.write_all(verifying_key.as_bytes())?;
|
2024-11-16 23:03:55 +08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_key(key_type: SecretKey) -> Result<[u8; 32], Box<dyn Error>> {
|
|
|
|
let path = env::current_dir()?
|
|
|
|
.join("assets")
|
|
|
|
.join("key")
|
|
|
|
.join(key_type.as_string());
|
2024-11-19 00:20:31 +08:00
|
|
|
let key_bytes = fs::read(path)?;
|
|
|
|
let mut key = [0u8; 32];
|
|
|
|
key.copy_from_slice(&key_bytes[..32]);
|
2024-11-16 23:03:55 +08:00
|
|
|
Ok(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn generate_jwt(claims: CustomClaims, duration: Duration) -> Result<String, Box<dyn Error>> {
|
2024-11-19 00:20:31 +08:00
|
|
|
let key_bytes = get_key(SecretKey::Signing)?;
|
|
|
|
let signing_key = SigningKey::from_bytes(&key_bytes);
|
2024-11-16 23:03:55 +08:00
|
|
|
|
|
|
|
let time_options = TimeOptions::new(
|
2024-11-19 00:20:31 +08:00
|
|
|
Duration::seconds(0),
|
|
|
|
Utc::now
|
|
|
|
);
|
|
|
|
let claims = jwt_compact::Claims::new(claims)
|
2024-11-16 23:03:55 +08:00
|
|
|
.set_duration_and_issuance(&time_options, duration)
|
2024-11-19 00:20:31 +08:00
|
|
|
.set_not_before(Utc::now());
|
2024-11-16 23:03:55 +08:00
|
|
|
|
2024-11-19 00:20:31 +08:00
|
|
|
let header = Header::empty();
|
2024-11-16 23:03:55 +08:00
|
|
|
|
2024-11-19 00:20:31 +08:00
|
|
|
let token = Ed25519.token(&header, &claims, &signing_key)?;
|
2024-11-16 23:03:55 +08:00
|
|
|
|
|
|
|
Ok(token)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn validate_jwt(token: &str) -> Result<CustomClaims, Box<dyn Error>> {
|
2024-11-19 00:20:31 +08:00
|
|
|
let key_bytes = get_key(SecretKey::Verifying)?;
|
|
|
|
let verifying = VerifyingKey::from_bytes(&key_bytes)?;
|
|
|
|
let token = UntrustedToken::new(token)?;
|
|
|
|
let token: Token<CustomClaims> = Ed25519.validator(&verifying).validate(&token)?;
|
2024-11-16 23:03:55 +08:00
|
|
|
|
|
|
|
let time_options = TimeOptions::new(
|
2024-11-19 00:20:31 +08:00
|
|
|
Duration::seconds(0),
|
|
|
|
Utc::now
|
|
|
|
);
|
2024-11-16 23:03:55 +08:00
|
|
|
token.claims()
|
2024-11-19 00:20:31 +08:00
|
|
|
.validate_expiration(&time_options)?
|
|
|
|
.validate_maturity(&time_options)?;
|
|
|
|
let claims = token.claims().custom.clone();
|
2024-11-16 23:03:55 +08:00
|
|
|
|
|
|
|
Ok(claims)
|
|
|
|
}
|