use crate::auth::AppState; use crate::entities::user; use axum::{ Json, extract::{Query, State}, response::{IntoResponse, Redirect, Response}, }; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set}; use serde::Deserialize; #[derive(Deserialize)] pub struct AuthCallback { pub code: String, #[allow(dead_code)] pub state: String, } pub async fn login(State(state): State) -> Response { let oidc = state.oidc; let (auth_url, _csrf_token, _nonce) = oidc.auth_url(); Redirect::to(&auth_url).into_response() } pub async fn callback( State(state): State, Query(params): Query, ) -> Response { let oidc = state.oidc; let db = state.db; match oidc.exchange_code(params.code).await { Ok(tokens) => match oidc.validate_token(&tokens.id_token).await { Ok(claims) => { let sub = claims.sub; let email = claims.email.unwrap_or_default(); let name = claims.name.unwrap_or_default(); let existing_user = user::Entity::find() .filter(user::Column::Sub.eq(&sub)) .one(&db) .await .unwrap_or(None); if existing_user.is_none() { let new_user = user::ActiveModel { sub: Set(sub), email: Set(email), name: Set(name), ..Default::default() }; new_user.insert(&db).await.unwrap(); }; let mut redirect_url = format!("/#access_token={}", tokens.id_token); if let Some(refresh_token) = tokens.refresh_token { redirect_url.push_str(&format!("&refresh_token={}", refresh_token)); } Redirect::to(&redirect_url).into_response() } Err(e) => { tracing::error!("Token validation error: {:?}", e); ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, "Invalid token", ) .into_response() } }, Err(e) => { tracing::error!("OIDC callback error: {:?}", e); ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, "Authentication failed", ) .into_response() } } } pub async fn refresh( State(state): State, Json(payload): Json, ) -> Response { let oidc = state.oidc; match oidc.refresh_token(payload.refresh_token).await { Ok(tokens) => Json(tokens).into_response(), Err(e) => { tracing::error!("Token refresh error: {:?}", e); (axum::http::StatusCode::UNAUTHORIZED, "Refresh failed").into_response() } } } #[derive(Deserialize)] pub struct RefreshRequest { pub refresh_token: String, } pub async fn logout() -> Response { Redirect::to("/").into_response() } pub async fn me( crate::auth::CurrentUser { id, sub, email, name, }: crate::auth::CurrentUser, ) -> Json { Json(crate::auth::CurrentUser { id, sub, email, name, }) }