this repo has no description
1use crate::state::AppState; 2use axum::{ 3 Json, 4 extract::{Query, State}, 5 http::StatusCode, 6 response::{IntoResponse, Response}, 7}; 8use bcrypt::verify; 9use serde::{Deserialize, Serialize}; 10use serde_json::json; 11use tracing::{error, info, warn}; 12 13#[derive(Deserialize)] 14pub struct GetServiceAuthParams { 15 pub aud: String, 16 pub lxm: Option<String>, 17 pub exp: Option<i64>, 18} 19 20#[derive(Serialize)] 21pub struct GetServiceAuthOutput { 22 pub token: String, 23} 24 25pub async fn get_service_auth( 26 State(state): State<AppState>, 27 headers: axum::http::HeaderMap, 28 Query(params): Query<GetServiceAuthParams>, 29) -> Response { 30 let auth_header = headers.get("Authorization"); 31 if auth_header.is_none() { 32 return ( 33 StatusCode::UNAUTHORIZED, 34 Json(json!({"error": "AuthenticationRequired"})), 35 ) 36 .into_response(); 37 } 38 39 let token = auth_header 40 .unwrap() 41 .to_str() 42 .unwrap_or("") 43 .replace("Bearer ", ""); 44 45 let session = sqlx::query!( 46 r#" 47 SELECT s.did, k.key_bytes 48 FROM sessions s 49 JOIN users u ON s.did = u.did 50 JOIN user_keys k ON u.id = k.user_id 51 WHERE s.access_jwt = $1 52 "#, 53 token 54 ) 55 .fetch_optional(&state.db) 56 .await; 57 58 let (did, key_bytes) = match session { 59 Ok(Some(row)) => (row.did, row.key_bytes), 60 Ok(None) => { 61 return ( 62 StatusCode::UNAUTHORIZED, 63 Json(json!({"error": "AuthenticationFailed"})), 64 ) 65 .into_response(); 66 } 67 Err(e) => { 68 error!("DB error in get_service_auth: {:?}", e); 69 return ( 70 StatusCode::INTERNAL_SERVER_ERROR, 71 Json(json!({"error": "InternalError"})), 72 ) 73 .into_response(); 74 } 75 }; 76 77 if let Err(_) = crate::auth::verify_token(&token, &key_bytes) { 78 return ( 79 StatusCode::UNAUTHORIZED, 80 Json(json!({"error": "AuthenticationFailed", "message": "Invalid token signature"})), 81 ) 82 .into_response(); 83 } 84 85 let lxm = params.lxm.as_deref().unwrap_or("*"); 86 87 let service_token = match crate::auth::create_service_token(&did, &params.aud, lxm, &key_bytes) 88 { 89 Ok(t) => t, 90 Err(e) => { 91 error!("Failed to create service token: {:?}", e); 92 return ( 93 StatusCode::INTERNAL_SERVER_ERROR, 94 Json(json!({"error": "InternalError"})), 95 ) 96 .into_response(); 97 } 98 }; 99 100 (StatusCode::OK, Json(GetServiceAuthOutput { token: service_token })).into_response() 101} 102 103#[derive(Deserialize)] 104pub struct CreateSessionInput { 105 pub identifier: String, 106 pub password: String, 107} 108 109#[derive(Serialize)] 110#[serde(rename_all = "camelCase")] 111pub struct CreateSessionOutput { 112 pub access_jwt: String, 113 pub refresh_jwt: String, 114 pub handle: String, 115 pub did: String, 116} 117 118pub async fn create_session( 119 State(state): State<AppState>, 120 Json(input): Json<CreateSessionInput>, 121) -> Response { 122 info!("create_session: identifier='{}'", input.identifier); 123 124 let user_row = sqlx::query!( 125 "SELECT u.id, 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", 126 input.identifier 127 ) 128 .fetch_optional(&state.db) 129 .await; 130 131 match user_row { 132 Ok(Some(row)) => { 133 let user_id = row.id; 134 let stored_hash = &row.password_hash; 135 let did = &row.did; 136 let handle = &row.handle; 137 let key_bytes = &row.key_bytes; 138 139 let password_valid = if verify(&input.password, stored_hash).unwrap_or(false) { 140 true 141 } else { 142 let app_pass_rows = sqlx::query!("SELECT password_hash FROM app_passwords WHERE user_id = $1", user_id) 143 .fetch_all(&state.db) 144 .await 145 .unwrap_or_default(); 146 147 app_pass_rows.iter().any(|row| { 148 verify(&input.password, &row.password_hash).unwrap_or(false) 149 }) 150 }; 151 152 if password_valid { 153 let access_jwt = match crate::auth::create_access_token(&did, &key_bytes) { 154 Ok(t) => t, 155 Err(e) => { 156 error!("Failed to create access token: {:?}", e); 157 return ( 158 StatusCode::INTERNAL_SERVER_ERROR, 159 Json(json!({"error": "InternalError"})), 160 ) 161 .into_response(); 162 } 163 }; 164 165 let refresh_jwt = match crate::auth::create_refresh_token(&did, &key_bytes) { 166 Ok(t) => t, 167 Err(e) => { 168 error!("Failed to create refresh token: {:?}", e); 169 return ( 170 StatusCode::INTERNAL_SERVER_ERROR, 171 Json(json!({"error": "InternalError"})), 172 ) 173 .into_response(); 174 } 175 }; 176 177 let session_insert = sqlx::query!( 178 "INSERT INTO sessions (access_jwt, refresh_jwt, did) VALUES ($1, $2, $3)", 179 access_jwt, 180 refresh_jwt, 181 did 182 ) 183 .execute(&state.db) 184 .await; 185 186 match session_insert { 187 Ok(_) => { 188 return ( 189 StatusCode::OK, 190 Json(CreateSessionOutput { 191 access_jwt, 192 refresh_jwt, 193 handle: handle.clone(), 194 did: did.clone(), 195 }), 196 ) 197 .into_response(); 198 } 199 Err(e) => { 200 error!("Failed to insert session: {:?}", e); 201 return ( 202 StatusCode::INTERNAL_SERVER_ERROR, 203 Json(json!({"error": "InternalError"})), 204 ) 205 .into_response(); 206 } 207 } 208 } else { 209 warn!( 210 "Password verification failed for identifier: {}", 211 input.identifier 212 ); 213 } 214 } 215 Ok(None) => { 216 warn!("User not found for identifier: {}", input.identifier); 217 } 218 Err(e) => { 219 error!("Database error fetching user: {:?}", e); 220 return ( 221 StatusCode::INTERNAL_SERVER_ERROR, 222 Json(json!({"error": "InternalError"})), 223 ) 224 .into_response(); 225 } 226 } 227 228 ( 229 StatusCode::UNAUTHORIZED, 230 Json(json!({"error": "AuthenticationFailed", "message": "Invalid identifier or password"})), 231 ) 232 .into_response() 233} 234 235pub async fn get_session( 236 State(state): State<AppState>, 237 headers: axum::http::HeaderMap, 238) -> Response { 239 let auth_header = headers.get("Authorization"); 240 if auth_header.is_none() { 241 return ( 242 StatusCode::UNAUTHORIZED, 243 Json(json!({"error": "AuthenticationRequired"})), 244 ) 245 .into_response(); 246 } 247 248 let token = auth_header 249 .unwrap() 250 .to_str() 251 .unwrap_or("") 252 .replace("Bearer ", ""); 253 254 let result = sqlx::query!( 255 r#" 256 SELECT u.handle, u.did, u.email, k.key_bytes 257 FROM sessions s 258 JOIN users u ON s.did = u.did 259 JOIN user_keys k ON u.id = k.user_id 260 WHERE s.access_jwt = $1 261 "#, 262 token 263 ) 264 .fetch_optional(&state.db) 265 .await; 266 267 match result { 268 Ok(Some(row)) => { 269 if let Err(_) = crate::auth::verify_token(&token, &row.key_bytes) { 270 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid token signature"}))).into_response(); 271 } 272 273 return ( 274 StatusCode::OK, 275 Json(json!({ 276 "handle": row.handle, 277 "did": row.did, 278 "email": row.email, 279 "didDoc": {} 280 })), 281 ) 282 .into_response(); 283 } 284 Ok(None) => { 285 return ( 286 StatusCode::UNAUTHORIZED, 287 Json(json!({"error": "AuthenticationFailed"})), 288 ) 289 .into_response(); 290 } 291 Err(e) => { 292 error!("Database error in get_session: {:?}", e); 293 return ( 294 StatusCode::INTERNAL_SERVER_ERROR, 295 Json(json!({"error": "InternalError"})), 296 ) 297 .into_response(); 298 } 299 } 300} 301 302pub async fn delete_session( 303 State(state): State<AppState>, 304 headers: axum::http::HeaderMap, 305) -> Response { 306 let auth_header = headers.get("Authorization"); 307 if auth_header.is_none() { 308 return ( 309 StatusCode::UNAUTHORIZED, 310 Json(json!({"error": "AuthenticationRequired"})), 311 ) 312 .into_response(); 313 } 314 315 let token = auth_header 316 .unwrap() 317 .to_str() 318 .unwrap_or("") 319 .replace("Bearer ", ""); 320 321 let result = sqlx::query!("DELETE FROM sessions WHERE access_jwt = $1", token) 322 .execute(&state.db) 323 .await; 324 325 match result { 326 Ok(res) => { 327 if res.rows_affected() > 0 { 328 return (StatusCode::OK, Json(json!({}))).into_response(); 329 } 330 } 331 Err(e) => { 332 error!("Database error in delete_session: {:?}", e); 333 } 334 } 335 336 ( 337 StatusCode::UNAUTHORIZED, 338 Json(json!({"error": "AuthenticationFailed"})), 339 ) 340 .into_response() 341} 342 343pub async fn refresh_session( 344 State(state): State<AppState>, 345 headers: axum::http::HeaderMap, 346) -> Response { 347 let auth_header = headers.get("Authorization"); 348 if auth_header.is_none() { 349 return ( 350 StatusCode::UNAUTHORIZED, 351 Json(json!({"error": "AuthenticationRequired"})), 352 ) 353 .into_response(); 354 } 355 356 let refresh_token = auth_header 357 .unwrap() 358 .to_str() 359 .unwrap_or("") 360 .replace("Bearer ", ""); 361 362 let session = sqlx::query!( 363 "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", 364 refresh_token 365 ) 366 .fetch_optional(&state.db) 367 .await; 368 369 match session { 370 Ok(Some(session_row)) => { 371 let did = &session_row.did; 372 let key_bytes = &session_row.key_bytes; 373 374 if let Err(_) = crate::auth::verify_token(&refresh_token, &key_bytes) { 375 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid refresh token signature"}))).into_response(); 376 } 377 378 let new_access_jwt = match crate::auth::create_access_token(&did, &key_bytes) { 379 Ok(t) => t, 380 Err(e) => { 381 error!("Failed to create access token: {:?}", e); 382 return ( 383 StatusCode::INTERNAL_SERVER_ERROR, 384 Json(json!({"error": "InternalError"})), 385 ) 386 .into_response(); 387 } 388 }; 389 let new_refresh_jwt = match crate::auth::create_refresh_token(&did, &key_bytes) { 390 Ok(t) => t, 391 Err(e) => { 392 error!("Failed to create refresh token: {:?}", e); 393 return ( 394 StatusCode::INTERNAL_SERVER_ERROR, 395 Json(json!({"error": "InternalError"})), 396 ) 397 .into_response(); 398 } 399 }; 400 401 let update = sqlx::query!( 402 "UPDATE sessions SET access_jwt = $1, refresh_jwt = $2 WHERE refresh_jwt = $3", 403 new_access_jwt, 404 new_refresh_jwt, 405 refresh_token 406 ) 407 .execute(&state.db) 408 .await; 409 410 match update { 411 Ok(_) => { 412 let user = sqlx::query!("SELECT handle FROM users WHERE did = $1", did) 413 .fetch_optional(&state.db) 414 .await; 415 416 match user { 417 Ok(Some(u)) => { 418 return ( 419 StatusCode::OK, 420 Json(json!({ 421 "accessJwt": new_access_jwt, 422 "refreshJwt": new_refresh_jwt, 423 "handle": u.handle, 424 "did": did 425 })), 426 ) 427 .into_response(); 428 } 429 Ok(None) => { 430 error!("User not found for existing session: {}", did); 431 return ( 432 StatusCode::INTERNAL_SERVER_ERROR, 433 Json(json!({"error": "InternalError"})), 434 ) 435 .into_response(); 436 } 437 Err(e) => { 438 error!("Database error fetching user: {:?}", e); 439 return ( 440 StatusCode::INTERNAL_SERVER_ERROR, 441 Json(json!({"error": "InternalError"})), 442 ) 443 .into_response(); 444 } 445 } 446 } 447 Err(e) => { 448 error!("Database error updating session: {:?}", e); 449 return ( 450 StatusCode::INTERNAL_SERVER_ERROR, 451 Json(json!({"error": "InternalError"})), 452 ) 453 .into_response(); 454 } 455 } 456 } 457 Ok(None) => { 458 return ( 459 StatusCode::UNAUTHORIZED, 460 Json(json!({"error": "AuthenticationFailed", "message": "Invalid refresh token"})), 461 ) 462 .into_response(); 463 } 464 Err(e) => { 465 error!("Database error fetching session: {:?}", e); 466 return ( 467 StatusCode::INTERNAL_SERVER_ERROR, 468 Json(json!({"error": "InternalError"})), 469 ) 470 .into_response(); 471 } 472 } 473}