A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at fix/spotify 168 lines 4.9 kB view raw
1use anyhow::Error; 2use jsonwebtoken::DecodingKey; 3use jsonwebtoken::EncodingKey; 4use jsonwebtoken::Header; 5use jsonwebtoken::Validation; 6use serde::{Deserialize, Serialize}; 7use sqlx::{Pool, Postgres}; 8use std::collections::BTreeMap; 9use std::env; 10 11use crate::cache::Cache; 12use crate::repo; 13use crate::signature::generate_signature; 14use crate::xata::user::User; 15 16#[derive(Debug, Serialize, Deserialize)] 17pub struct Claims { 18 pub exp: usize, 19 pub iat: usize, 20 pub did: String, 21} 22 23pub async fn authenticate_v1( 24 pool: &Pool<Postgres>, 25 api_key: &str, 26 timestamp: &str, 27 password_md5: &str, 28) -> Result<(), Error> { 29 match repo::user::get_user_by_apikey(pool, api_key).await? { 30 Some(user) => { 31 let shared_secret = user 32 .shared_secret 33 .ok_or_else(|| Error::msg("User does not have a shared secret"))?; 34 let hashed_password = md5::compute(format!("{}", shared_secret)); 35 let hashed_password = format!("{:x}", hashed_password); 36 let expected_password = format!("{}{}", hashed_password, timestamp); 37 let expected_password = md5::compute(expected_password); 38 let expected_password = format!("{:x}", expected_password); 39 if expected_password != password_md5 { 40 println!("{} != {}", expected_password, password_md5); 41 return Err(Error::msg("Invalid password")); 42 } 43 Ok(()) 44 } 45 None => Err(Error::msg("Invalid API key")), 46 } 47} 48 49pub async fn authenticate( 50 pool: &Pool<Postgres>, 51 api_key: &str, 52 api_sig: &str, 53 session_key: &str, 54 form: &BTreeMap<String, String>, 55) -> Result<(), Error> { 56 let claims = decode_token(session_key)?; 57 58 let user_apikey = repo::api_key::get_apikey(pool, api_key, &claims.did).await?; 59 60 if user_apikey.is_none() { 61 return Err(Error::msg("Invalid API key")); 62 } 63 64 let user_apikey = user_apikey.unwrap(); 65 66 let signature = generate_signature(form, &user_apikey.shared_secret); 67 68 if signature != api_sig { 69 return Err(Error::msg("Invalid signature")); 70 } 71 72 Ok(()) 73} 74 75pub async fn extract_did( 76 pool: &Pool<Postgres>, 77 form: &BTreeMap<String, String>, 78) -> Result<String, Error> { 79 let apikey = form 80 .get("api_key") 81 .ok_or_else(|| Error::msg("Missing api_key"))?; 82 let user = repo::user::get_user_by_apikey(pool, apikey).await?; 83 let did = user 84 .ok_or_else(|| Error::msg("Corresponding user not found"))? 85 .did; 86 Ok(did) 87} 88 89pub fn generate_token(did: &str) -> Result<String, Error> { 90 if env::var("JWT_SECRET").is_err() { 91 return Err(Error::msg("JWT_SECRET is not set")); 92 } 93 94 let claims = Claims { 95 exp: chrono::Utc::now().timestamp() as usize + 3600, 96 iat: chrono::Utc::now().timestamp() as usize, 97 did: did.to_string(), 98 }; 99 100 jsonwebtoken::encode( 101 &Header::default(), 102 &claims, 103 &EncodingKey::from_secret(env::var("JWT_SECRET")?.as_ref()), 104 ) 105 .map_err(Into::into) 106} 107 108pub fn decode_token(token: &str) -> Result<Claims, Error> { 109 if env::var("JWT_SECRET").is_err() { 110 return Err(Error::msg("JWT_SECRET is not set")); 111 } 112 113 jsonwebtoken::decode::<Claims>( 114 token, 115 &DecodingKey::from_secret(env::var("JWT_SECRET")?.as_ref()), 116 &Validation::default(), 117 ) 118 .map(|data| data.claims) 119 .map_err(Into::into) 120} 121 122pub async fn generate_session_id( 123 pool: &Pool<Postgres>, 124 cache: &Cache, 125 api_key: &str, 126) -> Result<String, Error> { 127 match repo::user::get_user_by_apikey(pool, &api_key).await? { 128 Some(user) => { 129 let mut bytes = [0u8; 16]; 130 rand::fill(&mut bytes[..]); 131 132 let session_id = hex::encode(bytes); 133 134 let user = 135 serde_json::to_string(&user).map_err(|_| Error::msg("Failed to serialize user"))?; 136 cache.set(&format!("lastfm:{}", session_id), &user)?; 137 Ok(session_id) 138 } 139 None => Err(Error::msg("Invalid API key")), 140 } 141} 142 143pub fn verify_session_id(cache: &Cache, session_id: &str) -> Result<String, Error> { 144 let user = cache.get(&format!("lastfm:{}", session_id))?; 145 if user.is_none() { 146 return Err(Error::msg("Session ID not found")); 147 } 148 let user: String = user.unwrap(); 149 let user: User = serde_json::from_str(&user) 150 .map_err(|e| Error::msg(format!("Failed to deserialize user: {}", e)))?; 151 Ok(user.xata_id) 152} 153 154#[cfg(test)] 155mod tests { 156 use dotenv::dotenv; 157 158 use super::*; 159 160 #[test] 161 fn test_generate_token() { 162 dotenv().ok(); 163 let token = generate_token("did:plc:7vdlgi2bflelz7mmuxoqjfcr").unwrap(); 164 let claims = decode_token(&token).unwrap(); 165 166 assert_eq!(claims.did, "did:plc:7vdlgi2bflelz7mmuxoqjfcr"); 167 } 168}