use axum::extract::{Query, State}; use axum_extra::extract::cookie::CookieJar; use serde::Deserialize; use crate::error::AuthError; use crate::features::AuthService; use crate::features::oauth::types::OAuthProvider; use crate::features::oauth::types::{CallbackCmd, OAuthLoginResp}; use export::data::AppResponseData; use super::super::cookies; #[derive(Debug, Deserialize, utoipa::IntoParams)] #[into_params(parameter_in = Query)] pub(crate) struct CallbackQuery { pub provider: OAuthProvider, pub code: String, pub state: String, } #[utoipa::path( get, path = "/auth/oauth/callback", tag = "auth", params(CallbackQuery), responses( (status = 200, description = "OAuth login completed. Returns token pair and the resolved user_id.", body = AppResponseData), (status = 400, description = "Missing state cookie or invalid state.", body = super::super::openapi::ErrorBody), (status = 401, description = "OAuth flow rejected.", body = super::super::openapi::ErrorBody), ), )] pub(crate) async fn handler( State(svc): State, jar: CookieJar, Query(q): Query, ) -> Result<(CookieJar, AppResponseData), AuthError> { let cookie_name = crate::features::oauth::usecase::cookie_name(&svc.config.cookie_name_prefix, q.provider); let temp_key_value = cookies::read_cookie_value(&jar, &cookie_name).ok_or_else(|| { AuthError::BadRequest(format!("missing oauth state cookie {cookie_name}")) })?; let cmd = CallbackCmd { provider: q.provider, code: q.code, state: q.state, temp_key_value, app_id: None, }; let resp = svc.oauth_callback(cmd).await?; let jar = cookies::remove_cookie(jar, &cookie_name); Ok((jar, AppResponseData::ok(resp))) }