this repo has no description
1mod common; 2mod helpers; 3 4use common::*; 5use helpers::*; 6 7use chrono::Utc; 8use reqwest::StatusCode; 9use serde_json::{Value, json}; 10 11#[tokio::test] 12async fn test_session_lifecycle_wrong_password() { 13 let client = client(); 14 let (_, _) = setup_new_user("session-wrong-pw").await; 15 16 let login_payload = json!({ 17 "identifier": format!("session-wrong-pw-{}.test", Utc::now().timestamp_millis()), 18 "password": "wrong-password" 19 }); 20 21 let res = client 22 .post(format!( 23 "{}/xrpc/com.atproto.server.createSession", 24 base_url().await 25 )) 26 .json(&login_payload) 27 .send() 28 .await 29 .expect("Failed to send request"); 30 31 assert!( 32 res.status() == StatusCode::UNAUTHORIZED || res.status() == StatusCode::BAD_REQUEST, 33 "Expected 401 or 400 for wrong password, got {}", 34 res.status() 35 ); 36} 37 38#[tokio::test] 39async fn test_session_lifecycle_multiple_sessions() { 40 let client = client(); 41 let ts = Utc::now().timestamp_millis(); 42 let handle = format!("multi-session-{}.test", ts); 43 let email = format!("multi-session-{}@test.com", ts); 44 let password = "multi-session-pw"; 45 46 let create_payload = json!({ 47 "handle": handle, 48 "email": email, 49 "password": password 50 }); 51 let create_res = client 52 .post(format!( 53 "{}/xrpc/com.atproto.server.createAccount", 54 base_url().await 55 )) 56 .json(&create_payload) 57 .send() 58 .await 59 .expect("Failed to create account"); 60 assert_eq!(create_res.status(), StatusCode::OK); 61 let create_body: Value = create_res.json().await.unwrap(); 62 let did = create_body["did"].as_str().unwrap(); 63 64 let _ = verify_new_account(&client, did).await; 65 66 let login_payload = json!({ 67 "identifier": handle, 68 "password": password 69 }); 70 71 let session1_res = client 72 .post(format!( 73 "{}/xrpc/com.atproto.server.createSession", 74 base_url().await 75 )) 76 .json(&login_payload) 77 .send() 78 .await 79 .expect("Failed session 1"); 80 assert_eq!(session1_res.status(), StatusCode::OK); 81 let session1: Value = session1_res.json().await.unwrap(); 82 let jwt1 = session1["accessJwt"].as_str().unwrap(); 83 84 let session2_res = client 85 .post(format!( 86 "{}/xrpc/com.atproto.server.createSession", 87 base_url().await 88 )) 89 .json(&login_payload) 90 .send() 91 .await 92 .expect("Failed session 2"); 93 assert_eq!(session2_res.status(), StatusCode::OK); 94 let session2: Value = session2_res.json().await.unwrap(); 95 let jwt2 = session2["accessJwt"].as_str().unwrap(); 96 97 assert_ne!(jwt1, jwt2, "Sessions should have different tokens"); 98 99 let get1 = client 100 .get(format!( 101 "{}/xrpc/com.atproto.server.getSession", 102 base_url().await 103 )) 104 .bearer_auth(jwt1) 105 .send() 106 .await 107 .expect("Failed getSession 1"); 108 assert_eq!(get1.status(), StatusCode::OK); 109 110 let get2 = client 111 .get(format!( 112 "{}/xrpc/com.atproto.server.getSession", 113 base_url().await 114 )) 115 .bearer_auth(jwt2) 116 .send() 117 .await 118 .expect("Failed getSession 2"); 119 assert_eq!(get2.status(), StatusCode::OK); 120} 121 122#[tokio::test] 123async fn test_session_lifecycle_refresh_invalidates_old() { 124 let client = client(); 125 let ts = Utc::now().timestamp_millis(); 126 let handle = format!("refresh-inv-{}.test", ts); 127 let email = format!("refresh-inv-{}@test.com", ts); 128 let password = "refresh-inv-pw"; 129 130 let create_payload = json!({ 131 "handle": handle, 132 "email": email, 133 "password": password 134 }); 135 let create_res = client 136 .post(format!( 137 "{}/xrpc/com.atproto.server.createAccount", 138 base_url().await 139 )) 140 .json(&create_payload) 141 .send() 142 .await 143 .expect("Failed to create account"); 144 let create_body: Value = create_res.json().await.unwrap(); 145 let did = create_body["did"].as_str().unwrap(); 146 147 let _ = verify_new_account(&client, did).await; 148 149 let login_payload = json!({ 150 "identifier": handle, 151 "password": password 152 }); 153 let login_res = client 154 .post(format!( 155 "{}/xrpc/com.atproto.server.createSession", 156 base_url().await 157 )) 158 .json(&login_payload) 159 .send() 160 .await 161 .expect("Failed login"); 162 let login_body: Value = login_res.json().await.unwrap(); 163 let refresh_jwt = login_body["refreshJwt"].as_str().unwrap().to_string(); 164 165 let refresh_res = client 166 .post(format!( 167 "{}/xrpc/com.atproto.server.refreshSession", 168 base_url().await 169 )) 170 .bearer_auth(&refresh_jwt) 171 .send() 172 .await 173 .expect("Failed first refresh"); 174 assert_eq!(refresh_res.status(), StatusCode::OK); 175 let refresh_body: Value = refresh_res.json().await.unwrap(); 176 let new_refresh_jwt = refresh_body["refreshJwt"].as_str().unwrap(); 177 178 assert_ne!(refresh_jwt, new_refresh_jwt, "Refresh tokens should differ"); 179 180 let reuse_res = client 181 .post(format!( 182 "{}/xrpc/com.atproto.server.refreshSession", 183 base_url().await 184 )) 185 .bearer_auth(&refresh_jwt) 186 .send() 187 .await 188 .expect("Failed reuse attempt"); 189 190 assert!( 191 reuse_res.status() == StatusCode::UNAUTHORIZED || reuse_res.status() == StatusCode::BAD_REQUEST, 192 "Old refresh token should be invalid after use" 193 ); 194} 195 196#[tokio::test] 197async fn test_app_password_lifecycle() { 198 let client = client(); 199 let ts = Utc::now().timestamp_millis(); 200 let handle = format!("apppass-{}.test", ts); 201 let email = format!("apppass-{}@test.com", ts); 202 let password = "apppass-password"; 203 204 let create_res = client 205 .post(format!( 206 "{}/xrpc/com.atproto.server.createAccount", 207 base_url().await 208 )) 209 .json(&json!({ 210 "handle": handle, 211 "email": email, 212 "password": password 213 })) 214 .send() 215 .await 216 .expect("Failed to create account"); 217 218 assert_eq!(create_res.status(), StatusCode::OK); 219 let account: Value = create_res.json().await.unwrap(); 220 let did = account["did"].as_str().unwrap(); 221 222 let jwt = verify_new_account(&client, did).await; 223 224 let create_app_pass_res = client 225 .post(format!( 226 "{}/xrpc/com.atproto.server.createAppPassword", 227 base_url().await 228 )) 229 .bearer_auth(&jwt) 230 .json(&json!({ "name": "Test App" })) 231 .send() 232 .await 233 .expect("Failed to create app password"); 234 235 assert_eq!(create_app_pass_res.status(), StatusCode::OK); 236 let app_pass: Value = create_app_pass_res.json().await.unwrap(); 237 let app_password = app_pass["password"].as_str().unwrap().to_string(); 238 assert_eq!(app_pass["name"], "Test App"); 239 240 let list_res = client 241 .get(format!( 242 "{}/xrpc/com.atproto.server.listAppPasswords", 243 base_url().await 244 )) 245 .bearer_auth(&jwt) 246 .send() 247 .await 248 .expect("Failed to list app passwords"); 249 250 assert_eq!(list_res.status(), StatusCode::OK); 251 let list_body: Value = list_res.json().await.unwrap(); 252 let passwords = list_body["passwords"].as_array().unwrap(); 253 assert_eq!(passwords.len(), 1); 254 assert_eq!(passwords[0]["name"], "Test App"); 255 256 let login_res = client 257 .post(format!( 258 "{}/xrpc/com.atproto.server.createSession", 259 base_url().await 260 )) 261 .json(&json!({ 262 "identifier": handle, 263 "password": app_password 264 })) 265 .send() 266 .await 267 .expect("Failed to login with app password"); 268 269 assert_eq!(login_res.status(), StatusCode::OK, "App password login should work"); 270 271 let revoke_res = client 272 .post(format!( 273 "{}/xrpc/com.atproto.server.revokeAppPassword", 274 base_url().await 275 )) 276 .bearer_auth(&jwt) 277 .json(&json!({ "name": "Test App" })) 278 .send() 279 .await 280 .expect("Failed to revoke app password"); 281 282 assert_eq!(revoke_res.status(), StatusCode::OK); 283 284 let login_after_revoke = client 285 .post(format!( 286 "{}/xrpc/com.atproto.server.createSession", 287 base_url().await 288 )) 289 .json(&json!({ 290 "identifier": handle, 291 "password": app_password 292 })) 293 .send() 294 .await 295 .expect("Failed to attempt login after revoke"); 296 297 assert!( 298 login_after_revoke.status() == StatusCode::UNAUTHORIZED 299 || login_after_revoke.status() == StatusCode::BAD_REQUEST, 300 "Revoked app password should not work" 301 ); 302 303 let list_after_revoke = client 304 .get(format!( 305 "{}/xrpc/com.atproto.server.listAppPasswords", 306 base_url().await 307 )) 308 .bearer_auth(&jwt) 309 .send() 310 .await 311 .expect("Failed to list after revoke"); 312 313 let list_after: Value = list_after_revoke.json().await.unwrap(); 314 let passwords_after = list_after["passwords"].as_array().unwrap(); 315 assert_eq!(passwords_after.len(), 0, "No app passwords should remain"); 316} 317 318#[tokio::test] 319async fn test_account_deactivation_lifecycle() { 320 let client = client(); 321 let ts = Utc::now().timestamp_millis(); 322 let handle = format!("deactivate-{}.test", ts); 323 let email = format!("deactivate-{}@test.com", ts); 324 let password = "deactivate-password"; 325 326 let create_res = client 327 .post(format!( 328 "{}/xrpc/com.atproto.server.createAccount", 329 base_url().await 330 )) 331 .json(&json!({ 332 "handle": handle, 333 "email": email, 334 "password": password 335 })) 336 .send() 337 .await 338 .expect("Failed to create account"); 339 340 assert_eq!(create_res.status(), StatusCode::OK); 341 let account: Value = create_res.json().await.unwrap(); 342 let did = account["did"].as_str().unwrap().to_string(); 343 344 let jwt = verify_new_account(&client, &did).await; 345 346 let (post_uri, _) = create_post(&client, &did, &jwt, "Post before deactivation").await; 347 let post_rkey = post_uri.split('/').last().unwrap(); 348 349 let status_before = client 350 .get(format!( 351 "{}/xrpc/com.atproto.server.checkAccountStatus", 352 base_url().await 353 )) 354 .bearer_auth(&jwt) 355 .send() 356 .await 357 .expect("Failed to check status"); 358 359 assert_eq!(status_before.status(), StatusCode::OK); 360 let status_body: Value = status_before.json().await.unwrap(); 361 assert_eq!(status_body["activated"], true); 362 363 let deactivate_res = client 364 .post(format!( 365 "{}/xrpc/com.atproto.server.deactivateAccount", 366 base_url().await 367 )) 368 .bearer_auth(&jwt) 369 .json(&json!({})) 370 .send() 371 .await 372 .expect("Failed to deactivate"); 373 374 assert_eq!(deactivate_res.status(), StatusCode::OK); 375 376 let get_post_res = client 377 .get(format!( 378 "{}/xrpc/com.atproto.repo.getRecord", 379 base_url().await 380 )) 381 .query(&[ 382 ("repo", did.as_str()), 383 ("collection", "app.bsky.feed.post"), 384 ("rkey", post_rkey), 385 ]) 386 .send() 387 .await 388 .expect("Failed to get post while deactivated"); 389 390 assert_eq!(get_post_res.status(), StatusCode::OK, "Records should still be readable"); 391 392 let activate_res = client 393 .post(format!( 394 "{}/xrpc/com.atproto.server.activateAccount", 395 base_url().await 396 )) 397 .bearer_auth(&jwt) 398 .json(&json!({})) 399 .send() 400 .await 401 .expect("Failed to reactivate"); 402 403 assert_eq!(activate_res.status(), StatusCode::OK); 404 405 let status_after_activate = client 406 .get(format!( 407 "{}/xrpc/com.atproto.server.checkAccountStatus", 408 base_url().await 409 )) 410 .bearer_auth(&jwt) 411 .send() 412 .await 413 .expect("Failed to check status after activate"); 414 415 assert_eq!(status_after_activate.status(), StatusCode::OK); 416 417 let (new_post_uri, _) = create_post(&client, &did, &jwt, "Post after reactivation").await; 418 assert!(!new_post_uri.is_empty(), "Should be able to post after reactivation"); 419} 420 421#[tokio::test] 422async fn test_service_auth_lifecycle() { 423 let client = client(); 424 let (did, jwt) = setup_new_user("service-auth-test").await; 425 426 let service_auth_res = client 427 .get(format!( 428 "{}/xrpc/com.atproto.server.getServiceAuth", 429 base_url().await 430 )) 431 .query(&[ 432 ("aud", "did:web:api.bsky.app"), 433 ("lxm", "com.atproto.repo.uploadBlob"), 434 ]) 435 .bearer_auth(&jwt) 436 .send() 437 .await 438 .expect("Failed to get service auth"); 439 440 assert_eq!(service_auth_res.status(), StatusCode::OK); 441 let auth_body: Value = service_auth_res.json().await.unwrap(); 442 let service_token = auth_body["token"].as_str().expect("No token in response"); 443 444 let parts: Vec<&str> = service_token.split('.').collect(); 445 assert_eq!(parts.len(), 3, "Service token should be a valid JWT"); 446 447 use base64::Engine; 448 let payload_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD 449 .decode(parts[1]) 450 .expect("Failed to decode JWT payload"); 451 let claims: Value = serde_json::from_slice(&payload_bytes).expect("Invalid JWT payload"); 452 453 assert_eq!(claims["iss"], did); 454 assert_eq!(claims["aud"], "did:web:api.bsky.app"); 455 assert_eq!(claims["lxm"], "com.atproto.repo.uploadBlob"); 456} 457 458#[tokio::test] 459async fn test_request_account_delete() { 460 let client = client(); 461 let (did, jwt) = setup_new_user("request-delete-test").await; 462 463 let res = client 464 .post(format!( 465 "{}/xrpc/com.atproto.server.requestAccountDelete", 466 base_url().await 467 )) 468 .bearer_auth(&jwt) 469 .send() 470 .await 471 .expect("Failed to request account deletion"); 472 473 assert_eq!(res.status(), StatusCode::OK); 474 475 let db_url = get_db_connection_string().await; 476 let pool = sqlx::PgPool::connect(&db_url).await.expect("Failed to connect to test DB"); 477 478 let row = sqlx::query!("SELECT token, expires_at FROM account_deletion_requests WHERE did = $1", did) 479 .fetch_optional(&pool) 480 .await 481 .expect("Failed to query DB"); 482 483 assert!(row.is_some(), "Deletion token should exist in DB"); 484 let row = row.unwrap(); 485 assert!(!row.token.is_empty(), "Token should not be empty"); 486 assert!(row.expires_at > Utc::now(), "Token should not be expired"); 487}