add jwt auth, fix session handling, clean up middlewares
This commit is contained in:
@@ -1,12 +1,91 @@
|
||||
use actix_web::{web, HttpResponse, Responder, Result};
|
||||
use actix_web::{web, HttpResponse, Responder, Result, http::header, cookie::Cookie};
|
||||
use actix_session::Session;
|
||||
use tera::Tera;
|
||||
use crate::models::user::{User, LoginCredentials, RegistrationData};
|
||||
use crate::models::user::{User, LoginCredentials, RegistrationData, UserRole};
|
||||
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use chrono::{Utc, Duration};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
// JWT Claims structure
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Claims {
|
||||
pub sub: String, // Subject (email)
|
||||
pub exp: usize, // Expiration time
|
||||
pub iat: usize, // Issued at
|
||||
pub role: String, // User role
|
||||
}
|
||||
|
||||
// JWT Secret key
|
||||
lazy_static! {
|
||||
static ref JWT_SECRET: String = std::env::var("JWT_SECRET").unwrap_or_else(|_| "your_jwt_secret_key".to_string());
|
||||
}
|
||||
|
||||
/// Controller for handling authentication-related routes
|
||||
pub struct AuthController;
|
||||
|
||||
impl AuthController {
|
||||
/// Generate a JWT token for a user
|
||||
fn generate_token(email: &str, role: &UserRole) -> Result<String, jsonwebtoken::errors::Error> {
|
||||
let role_str = match role {
|
||||
UserRole::Admin => "admin",
|
||||
UserRole::User => "user",
|
||||
};
|
||||
|
||||
let expiration = Utc::now()
|
||||
.checked_add_signed(Duration::hours(24))
|
||||
.expect("valid timestamp")
|
||||
.timestamp() as usize;
|
||||
|
||||
let claims = Claims {
|
||||
sub: email.to_owned(),
|
||||
exp: expiration,
|
||||
iat: Utc::now().timestamp() as usize,
|
||||
role: role_str.to_string(),
|
||||
};
|
||||
|
||||
encode(
|
||||
&Header::default(),
|
||||
&claims,
|
||||
&EncodingKey::from_secret(JWT_SECRET.as_bytes())
|
||||
)
|
||||
}
|
||||
|
||||
/// Validate a JWT token
|
||||
pub fn validate_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
|
||||
let validation = Validation::new(Algorithm::HS256);
|
||||
|
||||
let token_data = decode::<Claims>(
|
||||
token,
|
||||
&DecodingKey::from_secret(JWT_SECRET.as_bytes()),
|
||||
&validation
|
||||
)?;
|
||||
|
||||
Ok(token_data.claims)
|
||||
}
|
||||
|
||||
/// Extract token from session
|
||||
pub fn extract_token_from_session(session: &Session) -> Option<String> {
|
||||
session.get::<String>("auth_token").ok().flatten()
|
||||
}
|
||||
|
||||
/// Extract token from cookie
|
||||
pub fn extract_token_from_cookie(req: &actix_web::HttpRequest) -> Option<String> {
|
||||
req.cookie("auth_token").map(|c| c.value().to_string())
|
||||
}
|
||||
|
||||
/// Check if user is authenticated from session
|
||||
pub async fn is_authenticated(session: &Session) -> Option<Claims> {
|
||||
if let Some(token) = Self::extract_token_from_session(session) {
|
||||
match Self::validate_token(&token) {
|
||||
Ok(claims) => Some(claims),
|
||||
Err(_) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the login page
|
||||
pub async fn login_page(tmpl: web::Data<Tera>) -> Result<impl Responder> {
|
||||
let mut ctx = tera::Context::new();
|
||||
@@ -27,10 +106,7 @@ impl AuthController {
|
||||
session: Session,
|
||||
_tmpl: web::Data<Tera>
|
||||
) -> Result<impl Responder> {
|
||||
// In a real application, you would validate the credentials against a database
|
||||
// For this example, we'll use a hardcoded user
|
||||
|
||||
// Skip authentication check and always log in the user
|
||||
// For simplicity, always log in the user without checking credentials
|
||||
// Create a user object with admin role
|
||||
let mut test_user = User::new(
|
||||
"Admin User".to_string(),
|
||||
@@ -39,17 +115,29 @@ impl AuthController {
|
||||
|
||||
// Set the ID and admin role
|
||||
test_user.id = Some(1);
|
||||
test_user.role = crate::models::user::UserRole::Admin;
|
||||
test_user.role = UserRole::Admin;
|
||||
|
||||
// Generate JWT token
|
||||
let token = Self::generate_token(&test_user.email, &test_user.role)
|
||||
.map_err(|_| actix_web::error::ErrorInternalServerError("Failed to generate token"))?;
|
||||
|
||||
// Store user data in session
|
||||
let user_json = serde_json::to_string(&test_user).unwrap();
|
||||
if let Err(e) = session.insert("user", &user_json) {
|
||||
eprintln!("Session error: {}", e);
|
||||
}
|
||||
session.insert("user", &user_json)?;
|
||||
session.insert("auth_token", &token)?;
|
||||
|
||||
// Redirect to the home page
|
||||
// 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
|
||||
Ok(HttpResponse::Found()
|
||||
.append_header(("Location", "/"))
|
||||
.cookie(cookie)
|
||||
.append_header((header::LOCATION, "/"))
|
||||
.finish())
|
||||
}
|
||||
|
||||
@@ -81,15 +169,29 @@ impl AuthController {
|
||||
|
||||
// Set the ID and admin role
|
||||
user.id = Some(1);
|
||||
user.role = crate::models::user::UserRole::Admin;
|
||||
user.role = UserRole::Admin;
|
||||
|
||||
// Generate JWT token
|
||||
let token = Self::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).unwrap();
|
||||
session.insert("user", &user_json)?;
|
||||
session.insert("auth_token", &token)?;
|
||||
|
||||
// Redirect to the home page
|
||||
// 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
|
||||
Ok(HttpResponse::Found()
|
||||
.append_header(("Location", "/"))
|
||||
.cookie(cookie)
|
||||
.append_header((header::LOCATION, "/"))
|
||||
.finish())
|
||||
}
|
||||
|
||||
@@ -98,9 +200,17 @@ impl AuthController {
|
||||
// Clear the session
|
||||
session.purge();
|
||||
|
||||
// Redirect to the home page
|
||||
// Create an expired cookie to remove the JWT token
|
||||
let cookie = Cookie::build("auth_token", "")
|
||||
.path("/")
|
||||
.http_only(true)
|
||||
.max_age(actix_web::cookie::time::Duration::seconds(0))
|
||||
.finish();
|
||||
|
||||
// Redirect to the home page and clear the auth token cookie
|
||||
Ok(HttpResponse::Found()
|
||||
.append_header(("Location", "/"))
|
||||
.cookie(cookie)
|
||||
.append_header((header::LOCATION, "/"))
|
||||
.finish())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user