this repo has no description
1use axum::{ 2 Form, Json, 3 extract::{Query, State}, 4 http::{HeaderMap, header::SET_COOKIE}, 5 response::{IntoResponse, Redirect, Response, Html}, 6}; 7use chrono::Utc; 8use serde::{Deserialize, Serialize}; 9use subtle::ConstantTimeEq; 10use urlencoding::encode as url_encode; 11 12use crate::state::AppState; 13use crate::oauth::{Code, DeviceAccount, DeviceData, DeviceId, OAuthError, SessionId, db, templates}; 14use crate::notifications::{NotificationChannel, channel_display_name, enqueue_2fa_code}; 15 16const DEVICE_COOKIE_NAME: &str = "oauth_device_id"; 17 18fn extract_device_cookie(headers: &HeaderMap) -> Option<String> { 19 headers 20 .get("cookie") 21 .and_then(|v| v.to_str().ok()) 22 .and_then(|cookie_str| { 23 for cookie in cookie_str.split(';') { 24 let cookie = cookie.trim(); 25 if let Some(value) = cookie.strip_prefix(&format!("{}=", DEVICE_COOKIE_NAME)) { 26 return Some(value.to_string()); 27 } 28 } 29 None 30 }) 31} 32 33fn extract_client_ip(headers: &HeaderMap) -> String { 34 if let Some(forwarded) = headers.get("x-forwarded-for") { 35 if let Ok(value) = forwarded.to_str() { 36 if let Some(first_ip) = value.split(',').next() { 37 return first_ip.trim().to_string(); 38 } 39 } 40 } 41 42 if let Some(real_ip) = headers.get("x-real-ip") { 43 if let Ok(value) = real_ip.to_str() { 44 return value.trim().to_string(); 45 } 46 } 47 48 "0.0.0.0".to_string() 49} 50 51fn extract_user_agent(headers: &HeaderMap) -> Option<String> { 52 headers 53 .get("user-agent") 54 .and_then(|v| v.to_str().ok()) 55 .map(|s| s.to_string()) 56} 57 58fn make_device_cookie(device_id: &str) -> String { 59 format!( 60 "{}={}; Path=/oauth; HttpOnly; Secure; SameSite=Lax; Max-Age=31536000", 61 DEVICE_COOKIE_NAME, 62 device_id 63 ) 64} 65 66#[derive(Debug, Deserialize)] 67pub struct AuthorizeQuery { 68 pub request_uri: Option<String>, 69 pub client_id: Option<String>, 70 pub new_account: Option<bool>, 71} 72 73#[derive(Debug, Serialize)] 74pub struct AuthorizeResponse { 75 pub client_id: String, 76 pub client_name: Option<String>, 77 pub scope: Option<String>, 78 pub redirect_uri: String, 79 pub state: Option<String>, 80 pub login_hint: Option<String>, 81} 82 83#[derive(Debug, Deserialize)] 84pub struct AuthorizeSubmit { 85 pub request_uri: String, 86 pub username: String, 87 pub password: String, 88 #[serde(default)] 89 pub remember_device: bool, 90} 91 92#[derive(Debug, Deserialize)] 93pub struct AuthorizeSelectSubmit { 94 pub request_uri: String, 95 pub did: String, 96} 97 98fn wants_json(headers: &HeaderMap) -> bool { 99 headers 100 .get("accept") 101 .and_then(|v| v.to_str().ok()) 102 .map(|accept| accept.contains("application/json")) 103 .unwrap_or(false) 104} 105 106pub async fn authorize_get( 107 State(state): State<AppState>, 108 headers: HeaderMap, 109 Query(query): Query<AuthorizeQuery>, 110) -> Response { 111 let request_uri = match query.request_uri { 112 Some(uri) => uri, 113 None => { 114 if wants_json(&headers) { 115 return ( 116 axum::http::StatusCode::BAD_REQUEST, 117 Json(serde_json::json!({ 118 "error": "invalid_request", 119 "error_description": "Missing request_uri parameter. Use PAR to initiate authorization." 120 })), 121 ).into_response(); 122 } 123 return ( 124 axum::http::StatusCode::BAD_REQUEST, 125 Html(templates::error_page( 126 "invalid_request", 127 Some("Missing request_uri parameter. Use PAR to initiate authorization."), 128 )), 129 ).into_response(); 130 } 131 }; 132 133 let request_data = match db::get_authorization_request(&state.db, &request_uri).await { 134 Ok(Some(data)) => data, 135 Ok(None) => { 136 if wants_json(&headers) { 137 return ( 138 axum::http::StatusCode::BAD_REQUEST, 139 Json(serde_json::json!({ 140 "error": "invalid_request", 141 "error_description": "Invalid or expired request_uri. Please start a new authorization request." 142 })), 143 ).into_response(); 144 } 145 return ( 146 axum::http::StatusCode::BAD_REQUEST, 147 Html(templates::error_page( 148 "invalid_request", 149 Some("Invalid or expired request_uri. Please start a new authorization request."), 150 )), 151 ).into_response(); 152 } 153 Err(e) => { 154 if wants_json(&headers) { 155 return ( 156 axum::http::StatusCode::INTERNAL_SERVER_ERROR, 157 Json(serde_json::json!({ 158 "error": "server_error", 159 "error_description": format!("Database error: {:?}", e) 160 })), 161 ).into_response(); 162 } 163 return ( 164 axum::http::StatusCode::INTERNAL_SERVER_ERROR, 165 Html(templates::error_page( 166 "server_error", 167 Some(&format!("Database error: {:?}", e)), 168 )), 169 ).into_response(); 170 } 171 }; 172 173 if request_data.expires_at < Utc::now() { 174 let _ = db::delete_authorization_request(&state.db, &request_uri).await; 175 if wants_json(&headers) { 176 return ( 177 axum::http::StatusCode::BAD_REQUEST, 178 Json(serde_json::json!({ 179 "error": "invalid_request", 180 "error_description": "Authorization request has expired. Please start a new request." 181 })), 182 ).into_response(); 183 } 184 return ( 185 axum::http::StatusCode::BAD_REQUEST, 186 Html(templates::error_page( 187 "invalid_request", 188 Some("Authorization request has expired. Please start a new request."), 189 )), 190 ).into_response(); 191 } 192 193 if wants_json(&headers) { 194 return Json(AuthorizeResponse { 195 client_id: request_data.parameters.client_id.clone(), 196 client_name: None, 197 scope: request_data.parameters.scope.clone(), 198 redirect_uri: request_data.parameters.redirect_uri.clone(), 199 state: request_data.parameters.state.clone(), 200 login_hint: request_data.parameters.login_hint.clone(), 201 }).into_response(); 202 } 203 204 let force_new_account = query.new_account.unwrap_or(false); 205 206 if !force_new_account { 207 if let Some(device_id) = extract_device_cookie(&headers) { 208 if let Ok(accounts) = db::get_device_accounts(&state.db, &device_id).await { 209 if !accounts.is_empty() { 210 let device_accounts: Vec<DeviceAccount> = accounts 211 .into_iter() 212 .map(|row| DeviceAccount { 213 did: row.did, 214 handle: row.handle, 215 email: row.email, 216 last_used_at: row.last_used_at, 217 }) 218 .collect(); 219 220 return Html(templates::account_selector_page( 221 &request_data.parameters.client_id, 222 None, 223 &request_uri, 224 &device_accounts, 225 )).into_response(); 226 } 227 } 228 } 229 } 230 231 Html(templates::login_page( 232 &request_data.parameters.client_id, 233 None, 234 request_data.parameters.scope.as_deref(), 235 &request_uri, 236 None, 237 request_data.parameters.login_hint.as_deref(), 238 )).into_response() 239} 240 241pub async fn authorize_get_json( 242 State(state): State<AppState>, 243 Query(query): Query<AuthorizeQuery>, 244) -> Result<Json<AuthorizeResponse>, OAuthError> { 245 let request_uri = query.request_uri.ok_or_else(|| { 246 OAuthError::InvalidRequest("request_uri is required".to_string()) 247 })?; 248 249 let request_data = db::get_authorization_request(&state.db, &request_uri) 250 .await? 251 .ok_or_else(|| OAuthError::InvalidRequest("Invalid or expired request_uri".to_string()))?; 252 253 if request_data.expires_at < Utc::now() { 254 db::delete_authorization_request(&state.db, &request_uri).await?; 255 return Err(OAuthError::InvalidRequest("request_uri has expired".to_string())); 256 } 257 258 Ok(Json(AuthorizeResponse { 259 client_id: request_data.parameters.client_id.clone(), 260 client_name: None, 261 scope: request_data.parameters.scope.clone(), 262 redirect_uri: request_data.parameters.redirect_uri.clone(), 263 state: request_data.parameters.state.clone(), 264 login_hint: request_data.parameters.login_hint.clone(), 265 })) 266} 267 268pub async fn authorize_post( 269 State(state): State<AppState>, 270 headers: HeaderMap, 271 Form(form): Form<AuthorizeSubmit>, 272) -> Response { 273 let json_response = wants_json(&headers); 274 275 let request_data = match db::get_authorization_request(&state.db, &form.request_uri).await { 276 Ok(Some(data)) => data, 277 Ok(None) => { 278 if json_response { 279 return ( 280 axum::http::StatusCode::BAD_REQUEST, 281 Json(serde_json::json!({ 282 "error": "invalid_request", 283 "error_description": "Invalid or expired request_uri." 284 })), 285 ).into_response(); 286 } 287 return Html(templates::error_page( 288 "invalid_request", 289 Some("Invalid or expired request_uri. Please start a new authorization request."), 290 )).into_response(); 291 } 292 Err(e) => { 293 if json_response { 294 return ( 295 axum::http::StatusCode::INTERNAL_SERVER_ERROR, 296 Json(serde_json::json!({ 297 "error": "server_error", 298 "error_description": format!("Database error: {:?}", e) 299 })), 300 ).into_response(); 301 } 302 return Html(templates::error_page( 303 "server_error", 304 Some(&format!("Database error: {:?}", e)), 305 )).into_response(); 306 } 307 }; 308 309 if request_data.expires_at < Utc::now() { 310 let _ = db::delete_authorization_request(&state.db, &form.request_uri).await; 311 if json_response { 312 return ( 313 axum::http::StatusCode::BAD_REQUEST, 314 Json(serde_json::json!({ 315 "error": "invalid_request", 316 "error_description": "Authorization request has expired." 317 })), 318 ).into_response(); 319 } 320 return Html(templates::error_page( 321 "invalid_request", 322 Some("Authorization request has expired. Please start a new request."), 323 )).into_response(); 324 } 325 326 let show_login_error = |error_msg: &str, json: bool| -> Response { 327 if json { 328 return ( 329 axum::http::StatusCode::FORBIDDEN, 330 Json(serde_json::json!({ 331 "error": "access_denied", 332 "error_description": error_msg 333 })), 334 ).into_response(); 335 } 336 Html(templates::login_page( 337 &request_data.parameters.client_id, 338 None, 339 request_data.parameters.scope.as_deref(), 340 &form.request_uri, 341 Some(error_msg), 342 Some(&form.username), 343 )).into_response() 344 }; 345 346 let user = match sqlx::query!( 347 r#" 348 SELECT id, did, email, password_hash, two_factor_enabled, 349 preferred_notification_channel as "preferred_notification_channel: NotificationChannel", 350 deactivated_at, takedown_ref 351 FROM users 352 WHERE handle = $1 OR email = $1 353 "#, 354 form.username 355 ) 356 .fetch_optional(&state.db) 357 .await 358 { 359 Ok(Some(u)) => u, 360 Ok(None) => return show_login_error("Invalid handle/email or password.", json_response), 361 Err(_) => return show_login_error("An error occurred. Please try again.", json_response), 362 }; 363 364 if user.deactivated_at.is_some() { 365 return show_login_error("This account has been deactivated.", json_response); 366 } 367 368 if user.takedown_ref.is_some() { 369 return show_login_error("This account has been taken down.", json_response); 370 } 371 372 let password_valid = match bcrypt::verify(&form.password, &user.password_hash) { 373 Ok(valid) => valid, 374 Err(_) => return show_login_error("An error occurred. Please try again.", json_response), 375 }; 376 377 if !password_valid { 378 return show_login_error("Invalid handle/email or password.", json_response); 379 } 380 381 if user.two_factor_enabled { 382 let _ = db::delete_2fa_challenge_by_request_uri(&state.db, &form.request_uri).await; 383 384 match db::create_2fa_challenge(&state.db, &user.did, &form.request_uri).await { 385 Ok(challenge) => { 386 let hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 387 if let Err(e) = enqueue_2fa_code( 388 &state.db, 389 user.id, 390 &challenge.code, 391 &hostname, 392 ).await { 393 tracing::warn!( 394 did = %user.did, 395 error = %e, 396 "Failed to enqueue 2FA notification" 397 ); 398 } 399 400 let channel_name = channel_display_name(user.preferred_notification_channel); 401 let redirect_url = format!( 402 "/oauth/authorize/2fa?request_uri={}&channel={}", 403 url_encode(&form.request_uri), 404 url_encode(channel_name) 405 ); 406 return Redirect::temporary(&redirect_url).into_response(); 407 } 408 Err(_) => { 409 return show_login_error("An error occurred. Please try again.", json_response); 410 } 411 } 412 } 413 414 let code = Code::generate(); 415 let mut device_id: Option<String> = extract_device_cookie(&headers); 416 let mut new_cookie: Option<String> = None; 417 418 if form.remember_device { 419 let final_device_id = if let Some(existing_id) = &device_id { 420 existing_id.clone() 421 } else { 422 let new_id = DeviceId::generate(); 423 let device_data = DeviceData { 424 session_id: SessionId::generate().0, 425 user_agent: extract_user_agent(&headers), 426 ip_address: extract_client_ip(&headers), 427 last_seen_at: Utc::now(), 428 }; 429 430 if db::create_device(&state.db, &new_id.0, &device_data).await.is_ok() { 431 new_cookie = Some(make_device_cookie(&new_id.0)); 432 device_id = Some(new_id.0.clone()); 433 } 434 new_id.0 435 }; 436 437 let _ = db::upsert_account_device(&state.db, &user.did, &final_device_id).await; 438 } 439 440 if let Err(_) = db::update_authorization_request( 441 &state.db, 442 &form.request_uri, 443 &user.did, 444 device_id.as_deref(), 445 &code.0, 446 ) 447 .await 448 { 449 return show_login_error("An error occurred. Please try again.", json_response); 450 } 451 452 let redirect_url = build_success_redirect( 453 &request_data.parameters.redirect_uri, 454 &code.0, 455 request_data.parameters.state.as_deref(), 456 ); 457 458 let redirect = Redirect::temporary(&redirect_url); 459 460 if let Some(cookie) = new_cookie { 461 ([(SET_COOKIE, cookie)], redirect).into_response() 462 } else { 463 redirect.into_response() 464 } 465} 466 467pub async fn authorize_select( 468 State(state): State<AppState>, 469 headers: HeaderMap, 470 Form(form): Form<AuthorizeSelectSubmit>, 471) -> Response { 472 let request_data = match db::get_authorization_request(&state.db, &form.request_uri).await { 473 Ok(Some(data)) => data, 474 Ok(None) => { 475 return Html(templates::error_page( 476 "invalid_request", 477 Some("Invalid or expired request_uri. Please start a new authorization request."), 478 )).into_response(); 479 } 480 Err(_) => { 481 return Html(templates::error_page( 482 "server_error", 483 Some("An error occurred. Please try again."), 484 )).into_response(); 485 } 486 }; 487 488 if request_data.expires_at < Utc::now() { 489 let _ = db::delete_authorization_request(&state.db, &form.request_uri).await; 490 return Html(templates::error_page( 491 "invalid_request", 492 Some("Authorization request has expired. Please start a new request."), 493 )).into_response(); 494 } 495 496 let device_id = match extract_device_cookie(&headers) { 497 Some(id) => id, 498 None => { 499 return Html(templates::error_page( 500 "invalid_request", 501 Some("No device session found. Please sign in."), 502 )).into_response(); 503 } 504 }; 505 506 let account_valid = match db::verify_account_on_device(&state.db, &device_id, &form.did).await { 507 Ok(valid) => valid, 508 Err(_) => { 509 return Html(templates::error_page( 510 "server_error", 511 Some("An error occurred. Please try again."), 512 )).into_response(); 513 } 514 }; 515 516 if !account_valid { 517 return Html(templates::error_page( 518 "access_denied", 519 Some("This account is not available on this device. Please sign in."), 520 )).into_response(); 521 } 522 523 let user = match sqlx::query!( 524 r#" 525 SELECT id, two_factor_enabled, 526 preferred_notification_channel as "preferred_notification_channel: NotificationChannel" 527 FROM users 528 WHERE did = $1 529 "#, 530 form.did 531 ) 532 .fetch_optional(&state.db) 533 .await 534 { 535 Ok(Some(u)) => u, 536 Ok(None) => { 537 return Html(templates::error_page( 538 "access_denied", 539 Some("Account not found. Please sign in."), 540 )).into_response(); 541 } 542 Err(_) => { 543 return Html(templates::error_page( 544 "server_error", 545 Some("An error occurred. Please try again."), 546 )).into_response(); 547 } 548 }; 549 550 if user.two_factor_enabled { 551 let _ = db::delete_2fa_challenge_by_request_uri(&state.db, &form.request_uri).await; 552 553 match db::create_2fa_challenge(&state.db, &form.did, &form.request_uri).await { 554 Ok(challenge) => { 555 let hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 556 if let Err(e) = enqueue_2fa_code( 557 &state.db, 558 user.id, 559 &challenge.code, 560 &hostname, 561 ).await { 562 tracing::warn!( 563 did = %form.did, 564 error = %e, 565 "Failed to enqueue 2FA notification" 566 ); 567 } 568 569 let channel_name = channel_display_name(user.preferred_notification_channel); 570 let redirect_url = format!( 571 "/oauth/authorize/2fa?request_uri={}&channel={}", 572 url_encode(&form.request_uri), 573 url_encode(channel_name) 574 ); 575 return Redirect::temporary(&redirect_url).into_response(); 576 } 577 Err(_) => { 578 return Html(templates::error_page( 579 "server_error", 580 Some("An error occurred. Please try again."), 581 )).into_response(); 582 } 583 } 584 } 585 586 let _ = db::upsert_account_device(&state.db, &form.did, &device_id).await; 587 588 let code = Code::generate(); 589 590 if let Err(_) = db::update_authorization_request( 591 &state.db, 592 &form.request_uri, 593 &form.did, 594 Some(&device_id), 595 &code.0, 596 ) 597 .await 598 { 599 return Html(templates::error_page( 600 "server_error", 601 Some("An error occurred. Please try again."), 602 )).into_response(); 603 } 604 605 let redirect_url = build_success_redirect( 606 &request_data.parameters.redirect_uri, 607 &code.0, 608 request_data.parameters.state.as_deref(), 609 ); 610 611 Redirect::temporary(&redirect_url).into_response() 612} 613 614fn build_success_redirect(redirect_uri: &str, code: &str, state: Option<&str>) -> String { 615 let mut redirect_url = redirect_uri.to_string(); 616 617 let separator = if redirect_url.contains('?') { '&' } else { '?' }; 618 redirect_url.push(separator); 619 redirect_url.push_str(&format!("code={}", url_encode(code))); 620 621 if let Some(req_state) = state { 622 redirect_url.push_str(&format!("&state={}", url_encode(req_state))); 623 } 624 625 let pds_hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 626 redirect_url.push_str(&format!("&iss={}", url_encode(&format!("https://{}", pds_hostname)))); 627 628 redirect_url 629} 630 631#[derive(Debug, Serialize)] 632pub struct AuthorizeDenyResponse { 633 pub error: String, 634 pub error_description: String, 635} 636 637pub async fn authorize_deny( 638 State(state): State<AppState>, 639 Form(form): Form<AuthorizeDenyForm>, 640) -> Result<Response, OAuthError> { 641 let request_data = db::get_authorization_request(&state.db, &form.request_uri) 642 .await? 643 .ok_or_else(|| OAuthError::InvalidRequest("Invalid request_uri".to_string()))?; 644 645 db::delete_authorization_request(&state.db, &form.request_uri).await?; 646 647 let redirect_uri = &request_data.parameters.redirect_uri; 648 let mut redirect_url = redirect_uri.to_string(); 649 650 let separator = if redirect_url.contains('?') { '&' } else { '?' }; 651 redirect_url.push(separator); 652 redirect_url.push_str("error=access_denied"); 653 redirect_url.push_str("&error_description=User%20denied%20the%20request"); 654 655 if let Some(state) = &request_data.parameters.state { 656 redirect_url.push_str(&format!("&state={}", url_encode(state))); 657 } 658 659 Ok(Redirect::temporary(&redirect_url).into_response()) 660} 661 662#[derive(Debug, Deserialize)] 663pub struct AuthorizeDenyForm { 664 pub request_uri: String, 665} 666 667#[derive(Debug, Deserialize)] 668pub struct Authorize2faQuery { 669 pub request_uri: String, 670 pub channel: Option<String>, 671} 672 673#[derive(Debug, Deserialize)] 674pub struct Authorize2faSubmit { 675 pub request_uri: String, 676 pub code: String, 677} 678 679const MAX_2FA_ATTEMPTS: i32 = 5; 680 681pub async fn authorize_2fa_get( 682 State(state): State<AppState>, 683 Query(query): Query<Authorize2faQuery>, 684) -> Response { 685 let challenge = match db::get_2fa_challenge(&state.db, &query.request_uri).await { 686 Ok(Some(c)) => c, 687 Ok(None) => { 688 return Html(templates::error_page( 689 "invalid_request", 690 Some("No 2FA challenge found. Please start over."), 691 )).into_response(); 692 } 693 Err(_) => { 694 return Html(templates::error_page( 695 "server_error", 696 Some("An error occurred. Please try again."), 697 )).into_response(); 698 } 699 }; 700 701 if challenge.expires_at < Utc::now() { 702 let _ = db::delete_2fa_challenge(&state.db, challenge.id).await; 703 return Html(templates::error_page( 704 "invalid_request", 705 Some("2FA code has expired. Please start over."), 706 )).into_response(); 707 } 708 709 let _request_data = match db::get_authorization_request(&state.db, &query.request_uri).await { 710 Ok(Some(d)) => d, 711 Ok(None) => { 712 return Html(templates::error_page( 713 "invalid_request", 714 Some("Authorization request not found. Please start over."), 715 )).into_response(); 716 } 717 Err(_) => { 718 return Html(templates::error_page( 719 "server_error", 720 Some("An error occurred. Please try again."), 721 )).into_response(); 722 } 723 }; 724 725 let channel = query.channel.as_deref().unwrap_or("email"); 726 727 Html(templates::two_factor_page( 728 &query.request_uri, 729 channel, 730 None, 731 )).into_response() 732} 733 734pub async fn authorize_2fa_post( 735 State(state): State<AppState>, 736 headers: HeaderMap, 737 Form(form): Form<Authorize2faSubmit>, 738) -> Response { 739 let challenge = match db::get_2fa_challenge(&state.db, &form.request_uri).await { 740 Ok(Some(c)) => c, 741 Ok(None) => { 742 return Html(templates::error_page( 743 "invalid_request", 744 Some("No 2FA challenge found. Please start over."), 745 )).into_response(); 746 } 747 Err(_) => { 748 return Html(templates::error_page( 749 "server_error", 750 Some("An error occurred. Please try again."), 751 )).into_response(); 752 } 753 }; 754 755 if challenge.expires_at < Utc::now() { 756 let _ = db::delete_2fa_challenge(&state.db, challenge.id).await; 757 return Html(templates::error_page( 758 "invalid_request", 759 Some("2FA code has expired. Please start over."), 760 )).into_response(); 761 } 762 763 if challenge.attempts >= MAX_2FA_ATTEMPTS { 764 let _ = db::delete_2fa_challenge(&state.db, challenge.id).await; 765 return Html(templates::error_page( 766 "access_denied", 767 Some("Too many failed attempts. Please start over."), 768 )).into_response(); 769 } 770 771 let code_valid: bool = form.code.trim().as_bytes().ct_eq(challenge.code.as_bytes()).into(); 772 773 if !code_valid { 774 let _ = db::increment_2fa_attempts(&state.db, challenge.id).await; 775 776 let channel = match sqlx::query_scalar!( 777 r#"SELECT preferred_notification_channel as "channel: NotificationChannel" FROM users WHERE did = $1"#, 778 challenge.did 779 ) 780 .fetch_optional(&state.db) 781 .await 782 { 783 Ok(Some(ch)) => channel_display_name(ch).to_string(), 784 Ok(None) | Err(_) => "email".to_string(), 785 }; 786 787 let _request_data = match db::get_authorization_request(&state.db, &form.request_uri).await { 788 Ok(Some(d)) => d, 789 Ok(None) => { 790 return Html(templates::error_page( 791 "invalid_request", 792 Some("Authorization request not found. Please start over."), 793 )).into_response(); 794 } 795 Err(_) => { 796 return Html(templates::error_page( 797 "server_error", 798 Some("An error occurred. Please try again."), 799 )).into_response(); 800 } 801 }; 802 803 return Html(templates::two_factor_page( 804 &form.request_uri, 805 &channel, 806 Some("Invalid verification code. Please try again."), 807 )).into_response(); 808 } 809 810 let _ = db::delete_2fa_challenge(&state.db, challenge.id).await; 811 812 let request_data = match db::get_authorization_request(&state.db, &form.request_uri).await { 813 Ok(Some(d)) => d, 814 Ok(None) => { 815 return Html(templates::error_page( 816 "invalid_request", 817 Some("Authorization request not found."), 818 )).into_response(); 819 } 820 Err(_) => { 821 return Html(templates::error_page( 822 "server_error", 823 Some("An error occurred."), 824 )).into_response(); 825 } 826 }; 827 828 let code = Code::generate(); 829 let device_id = extract_device_cookie(&headers); 830 831 if let Err(_) = db::update_authorization_request( 832 &state.db, 833 &form.request_uri, 834 &challenge.did, 835 device_id.as_deref(), 836 &code.0, 837 ) 838 .await 839 { 840 return Html(templates::error_page( 841 "server_error", 842 Some("An error occurred. Please try again."), 843 )).into_response(); 844 } 845 846 let redirect_url = build_success_redirect( 847 &request_data.parameters.redirect_uri, 848 &code.0, 849 request_data.parameters.state.as_deref(), 850 ); 851 852 Redirect::temporary(&redirect_url).into_response() 853}