This repository has been archived on 2025-12-01. You can view files and clone it, but cannot push or open issues or pull requests.
Files
projectmycelium_old/src/controllers/gitea_auth.rs
2025-09-01 21:37:01 -04:00

217 lines
7.6 KiB
Rust

use actix_web::{web, HttpRequest, Responder, Result, cookie::Cookie};
use actix_session::Session;
use oauth2::{AuthorizationCode, CsrfToken, Scope, TokenResponse};
use reqwest::Client;
use crate::config::oauth::GiteaOAuthConfig;
use crate::utils::response_builder::ResponseBuilder;
use crate::models::user::{User, UserRole};
use crate::controllers::auth::AuthController;
/// Controller for handling Gitea authentication
pub struct GiteaAuthController;
impl GiteaAuthController {
/// Initiate the OAuth flow
pub async fn login(
oauth_config: web::Data<GiteaOAuthConfig>,
session: Session,
) -> Result<impl Responder> {
// Generate the authorization URL
let (auth_url, csrf_token) = oauth_config
.client
.authorize_url(CsrfToken::new_random)
.add_scope(Scope::new("read:user".to_string()))
.add_scope(Scope::new("user:email".to_string()))
.url();
// Store the CSRF token in the session
let csrf_secret = csrf_token.secret().to_string();
session.insert("oauth_csrf_token", &csrf_secret)?;
// Log all session data for debugging
// Check if the CSRF token was actually stored
if let Ok(Some(token)) = session.get::<String>("oauth_csrf_token") {
} else {
}
// Check for other session keys
if let Ok(Some(_)) = session.get::<String>("user") {
}
if let Ok(Some(_)) = session.get::<String>("auth_token") {
}
// Also store it in a cookie as a backup
let csrf_cookie = Cookie::build("oauth_csrf_token", csrf_secret.clone())
.path("/")
.http_only(true)
.secure(false) // Set to true in production with HTTPS
.max_age(actix_web::cookie::time::Duration::minutes(30))
.finish();
// Store in a non-http-only cookie as well for debugging
let csrf_cookie_debug = Cookie::build("oauth_csrf_token_debug", csrf_secret)
.path("/")
.http_only(false) // Accessible from JavaScript for debugging
.secure(false)
.max_age(actix_web::cookie::time::Duration::minutes(30))
.finish();
// Redirect to the authorization URL
ResponseBuilder::redirect(auth_url.to_string())
.cookie(csrf_cookie)
.cookie(csrf_cookie_debug)
.build()
}
/// Handle the OAuth callback
pub async fn callback(
oauth_config: web::Data<GiteaOAuthConfig>,
session: Session,
query: web::Query<CallbackQuery>,
req: HttpRequest,
) -> Result<impl Responder> {
// Log all cookies for debugging
if let Ok(cookie_iter) = req.cookies() {
for cookie in cookie_iter.iter() {
}
} else {
}
// Log all session data for debugging
// Check for CSRF token
if let Ok(Some(token)) = session.get::<String>("oauth_csrf_token") {
} else {
}
// Check for other session keys
if let Ok(Some(_)) = session.get::<String>("user") {
}
if let Ok(Some(_)) = session.get::<String>("auth_token") {
}
// Try to get the CSRF token from the session
let csrf_token_result = session.get::<String>("oauth_csrf_token")?;
// If not in session, try to get it from the cookie
let csrf_token = match csrf_token_result {
Some(token) => {
token
},
None => {
// Try to get from cookie
match req.cookie("oauth_csrf_token") {
Some(cookie) => {
let token = cookie.value().to_string();
token
},
None => {
// For debugging, let's accept the state parameter directly
query.state.clone()
// Uncomment this for production use
//
// return Err(actix_web::error::ErrorBadRequest("Missing CSRF token"));
}
}
}
};
if csrf_token != query.state {
// In production, uncomment the following:
//
// return Err(actix_web::error::ErrorBadRequest("Invalid CSRF token"));
}
// Exchange the authorization code for an access token
let token = oauth_config
.client
.exchange_code(AuthorizationCode::new(query.code.clone()))
.request_async(oauth2::reqwest::async_http_client)
.await
.map_err(|e| actix_web::error::ErrorInternalServerError(format!("Token exchange error: {}", e)))?;
// Get the user information from Gitea
let client = Client::new();
let user_info_url = format!("{}/api/v1/user", oauth_config.instance_url);
let access_token_secret = token.access_token().secret();
let response = client
.get(&user_info_url)
.bearer_auth(access_token_secret)
.send()
.await
.map_err(|e| actix_web::error::ErrorInternalServerError(format!("API request error: {}", e)))?;
let response_body = response.text().await.map_err(|e| actix_web::error::ErrorInternalServerError(format!("Failed to get response body: {}", e)))?;
let gitea_user: crate::config::oauth::GiteaUser = serde_json::from_str(&response_body)
.map_err(|e| actix_web::error::ErrorInternalServerError(format!("JSON parsing error: {}", e)))?;
// Create or update the user in your system
let mut user_builder = User::builder()
.id(gitea_user.id as i32)
.name(gitea_user.full_name.clone())
.email(gitea_user.email.clone())
.role(UserRole::User);
let user = user_builder
.build()
.unwrap();
// Generate JWT token
let token = AuthController::generate_token(&user.email, &user.role)
.map_err(|_| actix_web::error::ErrorInternalServerError("Failed to generate token"))?;
// Store user data in session
let user_json = serde_json::to_string(&user).unwrap();
session.insert("user", &user_json)?;
session.insert("auth_token", &token)?;
// Store user email for mock data lookup
session.insert("user_email", &user.email)?;
// Store user_id for cart operations
session.insert("user_id", &user.email)?; // Using email as user_id for now
// Transfer guest cart items to user cart if any exist
if let Ok(order_service) = crate::services::order::OrderService::builder().build() {
match order_service.transfer_guest_cart_to_user(&user.email, &session) {
Ok(items_transferred) => {
if items_transferred > 0 {
}
}
Err(e) => {
// Don't fail login if cart transfer fails, just log the error
}
}
} else {
}
// Create a cookie with the JWT token
let cookie = Cookie::build("auth_token", token)
.path("/")
.http_only(true)
.secure(false) // Set to true in production with HTTPS
.max_age(actix_web::cookie::time::Duration::hours(24))
.finish();
// Redirect to the home page with JWT token in cookie
ResponseBuilder::redirect("/")
.cookie(cookie)
.build()
}
}
/// Query parameters for the OAuth callback
#[derive(serde::Deserialize)]
pub struct CallbackQuery {
pub code: String,
pub state: String,
}