Format and split big files into smaller ones

This commit is contained in:
Lewis
2025-12-07 11:47:38 +02:00
parent 7b90694066
commit e2cc51f0b1
34 changed files with 3067 additions and 1776 deletions
+38
View File
@@ -0,0 +1,38 @@
use serde::{Deserialize, Serialize};
pub mod token;
pub mod verify;
pub use token::{create_access_token, create_refresh_token, create_service_token};
pub use verify::{get_did_from_token, verify_token};
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub iss: String,
pub sub: String,
pub aud: String,
pub exp: usize,
pub iat: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lxm: Option<String>,
pub jti: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Header {
pub alg: String,
pub typ: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UnsafeClaims {
pub iss: String,
pub sub: Option<String>,
}
// fancy boy TokenData equivalent for compatibility/structure
pub struct TokenData<T> {
pub claims: T,
}
+86
View File
@@ -0,0 +1,86 @@
use super::{Claims, Header};
use anyhow::Result;
use base64::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use chrono::{Duration, Utc};
use k256::ecdsa::{Signature, SigningKey, signature::Signer};
use uuid;
pub fn create_access_token(did: &str, key_bytes: &[u8]) -> Result<String> {
create_signed_token(did, "access", key_bytes, Duration::minutes(15))
}
pub fn create_refresh_token(did: &str, key_bytes: &[u8]) -> Result<String> {
create_signed_token(did, "refresh", key_bytes, Duration::days(7))
}
pub fn create_service_token(did: &str, aud: &str, lxm: &str, key_bytes: &[u8]) -> Result<String> {
let signing_key = SigningKey::from_slice(key_bytes)?;
let expiration = Utc::now()
.checked_add_signed(Duration::seconds(60))
.expect("valid timestamp")
.timestamp();
let claims = Claims {
iss: did.to_owned(),
sub: did.to_owned(),
aud: aud.to_owned(),
exp: expiration as usize,
iat: Utc::now().timestamp() as usize,
scope: None,
lxm: Some(lxm.to_string()),
jti: uuid::Uuid::new_v4().to_string(),
};
sign_claims(claims, &signing_key)
}
fn create_signed_token(
did: &str,
scope: &str,
key_bytes: &[u8],
duration: Duration,
) -> Result<String> {
let signing_key = SigningKey::from_slice(key_bytes)?;
let expiration = Utc::now()
.checked_add_signed(duration)
.expect("valid timestamp")
.timestamp();
let claims = Claims {
iss: did.to_owned(),
sub: did.to_owned(),
aud: format!(
"did:web:{}",
std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string())
),
exp: expiration as usize,
iat: Utc::now().timestamp() as usize,
scope: Some(scope.to_string()),
lxm: None,
jti: uuid::Uuid::new_v4().to_string(),
};
sign_claims(claims, &signing_key)
}
fn sign_claims(claims: Claims, key: &SigningKey) -> Result<String> {
let header = Header {
alg: "ES256K".to_string(),
typ: "JWT".to_string(),
};
let header_json = serde_json::to_string(&header)?;
let claims_json = serde_json::to_string(&claims)?;
let header_b64 = URL_SAFE_NO_PAD.encode(header_json);
let claims_b64 = URL_SAFE_NO_PAD.encode(claims_json);
let message = format!("{}.{}", header_b64, claims_b64);
let signature: Signature = key.sign(message.as_bytes());
let signature_b64 = URL_SAFE_NO_PAD.encode(signature.to_bytes());
Ok(format!("{}.{}", message, signature_b64))
}
+60
View File
@@ -0,0 +1,60 @@
use super::{Claims, TokenData, UnsafeClaims};
use anyhow::{Context, Result, anyhow};
use base64::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use chrono::Utc;
use k256::ecdsa::{Signature, SigningKey, VerifyingKey, signature::Verifier};
pub fn get_did_from_token(token: &str) -> Result<String, String> {
let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 {
return Err("Invalid token format".to_string());
}
let payload_bytes = URL_SAFE_NO_PAD
.decode(parts[1])
.map_err(|e| format!("Base64 decode failed: {}", e))?;
let claims: UnsafeClaims =
serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?;
Ok(claims.sub.unwrap_or(claims.iss))
}
pub fn verify_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> {
let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 {
return Err(anyhow!("Invalid token format"));
}
let header_b64 = parts[0];
let claims_b64 = parts[1];
let signature_b64 = parts[2];
let signature_bytes = URL_SAFE_NO_PAD
.decode(signature_b64)
.context("Base64 decode of signature failed")?;
let signature = Signature::from_slice(&signature_bytes)
.map_err(|e| anyhow!("Invalid signature format: {}", e))?;
let signing_key = SigningKey::from_slice(key_bytes)?;
let verifying_key = VerifyingKey::from(&signing_key);
let message = format!("{}.{}", header_b64, claims_b64);
verifying_key
.verify(message.as_bytes(), &signature)
.map_err(|e| anyhow!("Signature verification failed: {}", e))?;
let claims_bytes = URL_SAFE_NO_PAD
.decode(claims_b64)
.context("Base64 decode of claims failed")?;
let claims: Claims =
serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?;
let now = Utc::now().timestamp() as usize;
if claims.exp < now {
return Err(anyhow!("Token expired"));
}
Ok(TokenData { claims })
}