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