Implement complete full-stack authentication system

- Restructure project with separate frontend/backend architecture
- Create dedicated backend with Axum, SQLite, JWT authentication
- Implement real API endpoints for register/login/verify
- Update frontend to use HTTP requests instead of mock auth
- Add bcrypt password hashing and secure token generation
- Separate Cargo.toml files for frontend and backend builds
- Fix Trunk compilation by isolating WASM-incompatible dependencies
- Create demo user in database for easy testing
- Both servers running: frontend (8081), backend (3000)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-08-28 16:15:37 -04:00
parent 08c333dcba
commit 25bf194d19
12 changed files with 641 additions and 82 deletions

57
backend/src/handlers.rs Normal file
View File

@@ -0,0 +1,57 @@
use axum::{
extract::{Query, State},
http::{HeaderMap, StatusCode},
response::Json,
};
use serde::Deserialize;
use std::sync::Arc;
use crate::{AppState, models::{LoginRequest, RegisterRequest, AuthResponse, ApiError}};
#[derive(Deserialize)]
pub struct VerifyQuery {
pub token: String,
}
pub async fn register(
State(state): State<Arc<AppState>>,
Json(request): Json<RegisterRequest>,
) -> Result<Json<AuthResponse>, ApiError> {
let response = state.auth_service.register(request).await?;
Ok(Json(response))
}
pub async fn login(
State(state): State<Arc<AppState>>,
Json(request): Json<LoginRequest>,
) -> Result<Json<AuthResponse>, ApiError> {
let response = state.auth_service.login(request).await?;
Ok(Json(response))
}
pub async fn verify_token(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
) -> Result<Json<serde_json::Value>, ApiError> {
// Try to get token from Authorization header
let token = if let Some(auth_header) = headers.get("authorization") {
let auth_str = auth_header
.to_str()
.map_err(|_| ApiError::BadRequest("Invalid authorization header".to_string()))?;
if let Some(token) = auth_str.strip_prefix("Bearer ") {
token.to_string()
} else {
return Err(ApiError::BadRequest("Authorization header must start with 'Bearer '".to_string()));
}
} else {
return Err(ApiError::Unauthorized("Authorization header required".to_string()));
};
let user_info = state.auth_service.verify_token(&token).await?;
Ok(Json(serde_json::json!({
"valid": true,
"user": user_info
})))
}