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