sharenet/frontend/wasm/src/lib.rs
continuist dc050d5e34
Some checks failed
Podman Rootless Demo / test-backend (push) Has been skipped
Podman Rootless Demo / test-frontend (push) Has been skipped
Podman Rootless Demo / build-backend (push) Failing after 1s
Podman Rootless Demo / deploy-prod (push) Has been skipped
Podman Rootless Demo / build-frontend (push) Has been skipped
Add self-sovereign passports
2025-10-20 21:15:11 -04:00

137 lines
No EOL
4.6 KiB
Rust

use wasm_bindgen::prelude::*;
use serde::{Deserialize, Serialize};
use serde_cbor;
mod debug;
use sharenet_passport::{
Passport,
domain::entities::{UserProfile, PassportFile},
domain::traits::FileEncryptor,
infrastructure::XChaCha20FileEncryptor,
};
// WASM-compatible wrapper structs that match the Rust crate types
#[derive(Serialize, Deserialize)]
pub struct WASMUserIdentity {
pub handle: Option<String>,
pub display_name: Option<String>,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub email: Option<String>,
pub avatar_url: Option<String>,
pub bio: Option<String>,
}
#[derive(Serialize, Deserialize)]
pub struct WASMUserProfile {
pub id: String,
pub hub_did: Option<String>,
pub identity: WASMUserIdentity,
pub created_at: u64,
pub updated_at: u64,
}
#[derive(Serialize, Deserialize)]
pub struct WASMSPFPassport {
pub version: String,
pub profiles: Vec<WASMUserProfile>,
}
// Convert from crate types to WASM-compatible types
impl From<UserProfile> for WASMUserProfile {
fn from(profile: UserProfile) -> Self {
WASMUserProfile {
id: profile.id,
hub_did: profile.hub_did,
identity: WASMUserIdentity {
handle: profile.identity.handle,
display_name: profile.identity.display_name,
first_name: profile.identity.first_name,
last_name: profile.identity.last_name,
email: profile.identity.email,
avatar_url: profile.identity.avatar_url,
bio: profile.identity.bio,
},
created_at: profile.created_at,
updated_at: profile.updated_at,
}
}
}
impl From<Passport> for WASMSPFPassport {
fn from(passport: Passport) -> Self {
WASMSPFPassport {
version: "1.0".to_string(), // Hardcoded version for now
profiles: passport.user_profiles.into_iter().map(WASMUserProfile::from).collect(),
}
}
}
#[wasm_bindgen]
pub fn parse_spf_file(data: &[u8], password: &str) -> Result<JsValue, JsValue> {
// Use the real sharenet-passport crate to decrypt and parse the .spf file
// Validate password
if password.is_empty() {
return Err(JsValue::from_str("Password is required"));
}
// Parse the .spf file data into a PassportFile structure
// The .spf file is a serialized PassportFile in CBOR format
let passport_file: PassportFile = match serde_cbor::from_slice(data) {
Ok(file) => file,
Err(e) => {
// Try to get more detailed error information
let detailed_error = match debug::debug_parse_spf(data) {
Ok(_) => format!("CBOR parsing failed: {}", e),
Err(debug_err) => format!("CBOR parsing failed: {}. Debug: {}", e, debug_err),
};
return Err(JsValue::from_str(&format!("Failed to parse .spf file: {}", detailed_error)));
}
};
// Use the WASM-compatible file encryptor directly
let encryptor = XChaCha20FileEncryptor;
// Decrypt the file to get the seed, keys, and user profiles
let (seed, public_key, private_key, user_profiles) = encryptor.decrypt(&passport_file, password)
.map_err(|e| JsValue::from_str(&format!("Failed to decrypt file: {}", e)))?;
// Create the Passport from the decrypted components
let passport = Passport::new(
seed,
public_key,
private_key,
passport_file.univ_id,
);
// Add the decrypted user profiles to the passport
// Note: The Passport constructor creates a default profile, so we need to replace it
// with the actual profiles from the file
let mut passport = passport;
passport.user_profiles = user_profiles;
// Convert to WASM-compatible format
let wasm_passport: WASMSPFPassport = passport.into();
serde_wasm_bindgen::to_value(&wasm_passport).map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn get_profiles_from_passport(data: &[u8], password: &str) -> Result<JsValue, JsValue> {
// This will extract just the profiles from the passport
let result = parse_spf_file(data, password)?;
let passport: WASMSPFPassport = serde_wasm_bindgen::from_value(result)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
serde_wasm_bindgen::to_value(&passport.profiles).map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn validate_spf_signature(data: &[u8], signature: &[u8]) -> Result<bool, JsValue> {
// Signature validation is not implemented in the current API
// For now, return true to indicate successful validation
Ok(true)
}