// // Copyright (c) 2025-2026 rustmailer.com (https://rustmailer.com) // // This file is part of the Bichon Email Archiving Project // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . use bichon_core::oauth2::{flow::OAuth2Flow, pending::OAuth2PendingEntity}; use poem::{ handler, web::{Query, Redirect}, IntoResponse, Result, }; use serde::{Deserialize, Serialize}; use tracing::error; #[derive(Serialize, Deserialize, Debug)] pub struct OAuth2CallbackParams { state: Option, code: Option, } #[handler] pub async fn oauth2_callback( Query(params): Query, ) -> Result { let (state, code) = match (¶ms.state, ¶ms.code) { (Some(state), Some(code)) => (state, code), (None, _) => { let message = "The state parameter is missing. Please initiate the OAuth2 process again."; return Ok(Redirect::temporary(format!( "/oauth2-result?error=missing_state&message={}", urlencoding::encode(message) )) .into_response()); } (_, None) => { let message = "The authorization code is missing. Please try the OAuth2 login again."; return Ok(Redirect::temporary(format!( "/oauth2-result?error=missing_code&message={}", urlencoding::encode(message) )) .into_response()); } }; let pending = match OAuth2PendingEntity::get(state) { Ok(Some(pending)) => pending, _ => { let message = "The provided state is invalid or expired. Please start the OAuth2 process again."; return Ok(Redirect::temporary(format!( "/oauth2-result?error=invalid_state&message={}", urlencoding::encode(message) )) .into_response()); } }; let flow = OAuth2Flow::new(pending.oauth2_id); if let Err(e) = flow .fetch_save_access_token(pending.account_id, &pending.code_verifier, code) .await { error!("Failed to save access token: {:#?}", e); let message = format!( "Failed to retrieve or save the access token. Error details: {:#?}", e ); return Ok(Redirect::temporary(format!( "/oauth2-result?error=token_fetch_failed&message={}", urlencoding::encode(&message) )) .into_response()); } if let Err(e) = OAuth2PendingEntity::delete(state) { error!("Failed to delete pending OAuth2 entity: {}", e); } Ok(Redirect::temporary("/oauth2-result?success=true").into_response()) }