this repo has no description
1use axum::{ 2 extract::State, 3 Json, 4 response::{IntoResponse, Response}, 5 http::StatusCode, 6}; 7use serde::{Deserialize, Serialize}; 8use serde_json::json; 9use crate::state::AppState; 10use sqlx::Row; 11use bcrypt::verify; 12use tracing::{info, error, warn}; 13 14pub async fn describe_server() -> impl IntoResponse { 15 let domains_str = std::env::var("AVAILABLE_USER_DOMAINS").unwrap_or_else(|_| "example.com".to_string()); 16 let domains: Vec<&str> = domains_str.split(',').map(|s| s.trim()).collect(); 17 18 Json(json!({ 19 "availableUserDomains": domains 20 })) 21} 22 23pub async fn health(State(state): State<AppState>) -> impl IntoResponse { 24 match sqlx::query("SELECT 1").execute(&state.db).await { 25 Ok(_) => (StatusCode::OK, "OK"), 26 Err(e) => { 27 error!("Health check failed: {:?}", e); 28 (StatusCode::SERVICE_UNAVAILABLE, "Service Unavailable") 29 } 30 } 31} 32 33#[derive(Deserialize)] 34pub struct CreateSessionInput { 35 pub identifier: String, 36 pub password: String, 37} 38 39#[derive(Serialize)] 40#[serde(rename_all = "camelCase")] 41pub struct CreateSessionOutput { 42 pub access_jwt: String, 43 pub refresh_jwt: String, 44 pub handle: String, 45 pub did: String, 46} 47 48pub async fn create_session( 49 State(state): State<AppState>, 50 Json(input): Json<CreateSessionInput>, 51) -> Response { 52 info!("create_session: identifier='{}'", input.identifier); 53 54 let user_row = sqlx::query("SELECT u.did, u.handle, u.password_hash, k.key_bytes FROM users u JOIN user_keys k ON u.id = k.user_id WHERE u.handle = $1 OR u.email = $1") 55 .bind(&input.identifier) 56 .fetch_optional(&state.db) 57 .await; 58 59 match user_row { 60 Ok(Some(row)) => { 61 let stored_hash: String = row.get("password_hash"); 62 63 if verify(&input.password, &stored_hash).unwrap_or(false) { 64 let did: String = row.get("did"); 65 let handle: String = row.get("handle"); 66 let key_bytes: Vec<u8> = row.get("key_bytes"); 67 68 let access_jwt = match crate::auth::create_access_token(&did, &key_bytes) { 69 Ok(t) => t, 70 Err(e) => { 71 error!("Failed to create access token: {:?}", e); 72 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 73 } 74 }; 75 76 let refresh_jwt = match crate::auth::create_refresh_token(&did, &key_bytes) { 77 Ok(t) => t, 78 Err(e) => { 79 error!("Failed to create refresh token: {:?}", e); 80 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 81 } 82 }; 83 84 let session_insert = sqlx::query("INSERT INTO sessions (access_jwt, refresh_jwt, did) VALUES ($1, $2, $3)") 85 .bind(&access_jwt) 86 .bind(&refresh_jwt) 87 .bind(&did) 88 .execute(&state.db) 89 .await; 90 91 match session_insert { 92 Ok(_) => { 93 return (StatusCode::OK, Json(CreateSessionOutput { 94 access_jwt, 95 refresh_jwt, 96 handle, 97 did, 98 })).into_response(); 99 }, 100 Err(e) => { 101 error!("Failed to insert session: {:?}", e); 102 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 103 } 104 } 105 } else { 106 warn!("Password verification failed for identifier: {}", input.identifier); 107 } 108 }, 109 Ok(None) => { 110 warn!("User not found for identifier: {}", input.identifier); 111 }, 112 Err(e) => { 113 error!("Database error fetching user: {:?}", e); 114 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 115 } 116 } 117 118 (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid identifier or password"}))).into_response() 119} 120 121pub async fn get_session( 122 State(state): State<AppState>, 123 headers: axum::http::HeaderMap, 124) -> Response { 125 let auth_header = headers.get("Authorization"); 126 if auth_header.is_none() { 127 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationRequired"}))).into_response(); 128 } 129 130 let token = auth_header.unwrap().to_str().unwrap_or("").replace("Bearer ", ""); 131 132 let result = sqlx::query( 133 r#" 134 SELECT u.handle, u.did, u.email, k.key_bytes 135 FROM sessions s 136 JOIN users u ON s.did = u.did 137 JOIN user_keys k ON u.id = k.user_id 138 WHERE s.access_jwt = $1 139 "# 140 ) 141 .bind(&token) 142 .fetch_optional(&state.db) 143 .await; 144 145 match result { 146 Ok(Some(row)) => { 147 let handle: String = row.get("handle"); 148 let did: String = row.get("did"); 149 let email: String = row.get("email"); 150 let key_bytes: Vec<u8> = row.get("key_bytes"); 151 152 if let Err(_) = crate::auth::verify_token(&token, &key_bytes) { 153 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid token signature"}))).into_response(); 154 } 155 156 return (StatusCode::OK, Json(json!({ 157 "handle": handle, 158 "did": did, 159 "email": email, 160 "didDoc": {} 161 }))).into_response(); 162 }, 163 Ok(None) => { 164 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed"}))).into_response(); 165 }, 166 Err(e) => { 167 error!("Database error in get_session: {:?}", e); 168 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 169 } 170 } 171} 172 173pub async fn delete_session( 174 State(state): State<AppState>, 175 headers: axum::http::HeaderMap, 176) -> Response { 177 let auth_header = headers.get("Authorization"); 178 if auth_header.is_none() { 179 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationRequired"}))).into_response(); 180 } 181 182 let token = auth_header.unwrap().to_str().unwrap_or("").replace("Bearer ", ""); 183 184 let result = sqlx::query("DELETE FROM sessions WHERE access_jwt = $1") 185 .bind(token) 186 .execute(&state.db) 187 .await; 188 189 match result { 190 Ok(res) => { 191 if res.rows_affected() > 0 { 192 return (StatusCode::OK, Json(json!({}))).into_response(); 193 } 194 }, 195 Err(e) => { 196 error!("Database error in delete_session: {:?}", e); 197 } 198 } 199 200 (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed"}))).into_response() 201} 202 203pub async fn refresh_session( 204 State(state): State<AppState>, 205 headers: axum::http::HeaderMap, 206) -> Response { 207 let auth_header = headers.get("Authorization"); 208 if auth_header.is_none() { 209 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationRequired"}))).into_response(); 210 } 211 212 let refresh_token = auth_header.unwrap().to_str().unwrap_or("").replace("Bearer ", ""); 213 214 let session = sqlx::query( 215 "SELECT s.did, k.key_bytes FROM sessions s JOIN users u ON s.did = u.did JOIN user_keys k ON u.id = k.user_id WHERE s.refresh_jwt = $1" 216 ) 217 .bind(&refresh_token) 218 .fetch_optional(&state.db) 219 .await; 220 221 match session { 222 Ok(Some(session_row)) => { 223 let did: String = session_row.get("did"); 224 let key_bytes: Vec<u8> = session_row.get("key_bytes"); 225 226 if let Err(_) = crate::auth::verify_token(&refresh_token, &key_bytes) { 227 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid refresh token signature"}))).into_response(); 228 } 229 230 let new_access_jwt = match crate::auth::create_access_token(&did, &key_bytes) { 231 Ok(t) => t, 232 Err(e) => { 233 error!("Failed to create access token: {:?}", e); 234 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 235 } 236 }; 237 let new_refresh_jwt = match crate::auth::create_refresh_token(&did, &key_bytes) { 238 Ok(t) => t, 239 Err(e) => { 240 error!("Failed to create refresh token: {:?}", e); 241 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 242 } 243 }; 244 245 let update = sqlx::query("UPDATE sessions SET access_jwt = $1, refresh_jwt = $2 WHERE refresh_jwt = $3") 246 .bind(&new_access_jwt) 247 .bind(&new_refresh_jwt) 248 .bind(&refresh_token) 249 .execute(&state.db) 250 .await; 251 252 match update { 253 Ok(_) => { 254 let user = sqlx::query("SELECT handle FROM users WHERE did = $1") 255 .bind(&did) 256 .fetch_optional(&state.db) 257 .await; 258 259 match user { 260 Ok(Some(u)) => { 261 let handle: String = u.get("handle"); 262 return (StatusCode::OK, Json(json!({ 263 "accessJwt": new_access_jwt, 264 "refreshJwt": new_refresh_jwt, 265 "handle": handle, 266 "did": did 267 }))).into_response(); 268 }, 269 Ok(None) => { 270 error!("User not found for existing session: {}", did); 271 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 272 }, 273 Err(e) => { 274 error!("Database error fetching user: {:?}", e); 275 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 276 } 277 } 278 }, 279 Err(e) => { 280 error!("Database error updating session: {:?}", e); 281 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 282 } 283 } 284 }, 285 Ok(None) => { 286 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid refresh token"}))).into_response(); 287 }, 288 Err(e) => { 289 error!("Database error fetching session: {:?}", e); 290 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError"}))).into_response(); 291 } 292 } 293} 294