This commit is contained in:
2026-06-28 18:12:00 +02:00
parent 5152ec0f7e
commit 7a593fc204
27 changed files with 413 additions and 100 deletions
-2
View File
@@ -36,13 +36,11 @@ pub fn create_jwt(
}
pub fn verify_jwt(token: &str, secret: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
println!("Verifying token: {}", token);
let validation = Validation::default();
let token_data = decode::<Claims>(
token,
&DecodingKey::from_secret(secret.as_ref()),
&validation,
)?;
println!("Token data: {:?}", token_data);
Ok(token_data.claims)
}
+6
View File
@@ -5,6 +5,7 @@ use axum::{
middleware::Next,
response::{IntoResponse, Response},
};
use axum_extra::extract::CookieJar;
use std::sync::Arc;
use std::time::Instant;
use tracing::{info, Instrument};
@@ -103,6 +104,11 @@ pub async fn auth_middleware(
None
}
})
.or_else(|| {
// Grâce à CookieJar, on extrait proprement le cookie "token" ou "jwt"
let jar = CookieJar::from_headers(req.headers());
jar.get("token").map(|cookie| cookie.value().to_string())
})
.or_else(|| {
req.uri().query().and_then(|q| {
form_urlencoded::parse(q.as_bytes())
+2 -1
View File
@@ -32,7 +32,8 @@ pub struct Model {
pub name: Option<String>,
pub created_at: DateTimeUtc,
pub updated_at: DateTimeUtc,
pub default_permissions: Option<u64>,
pub default_channel_permissions: Option<i64>,
pub default_voice_permissions: Option<i64>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
+68 -3
View File
@@ -6,11 +6,13 @@ use crate::http::error::HTTPError;
use crate::routes::user::mapper::user_model_to_user_response;
use axum::extract::State;
use axum::Json;
use axum_extra::extract::cookie::{Cookie, SameSite};
use axum_extra::extract::CookieJar;
use sea_orm::ActiveModelBehavior;
#[utoipa::path(
get,
path = "/auth/login",
post,
path = "/auth/bearer-login",
request_body = LoginRequest,
responses(
(status = 200, description = "Login successful", body = LoginResponse),
@@ -18,7 +20,7 @@ use sea_orm::ActiveModelBehavior;
),
tag = "Auth"
)]
pub async fn login_user_pw(
pub async fn login_bearer(
State(state): State<AppState>,
Json(payload): Json<LoginRequest>,
) -> Result<Json<LoginResponse>, HTTPError> {
@@ -41,6 +43,69 @@ pub async fn login_user_pw(
Ok(Json(LoginResponse { token }))
}
#[utoipa::path(
post,
path = "/auth/login",
request_body = LoginRequest,
responses(
(status = 200, description = "Login successful with cookie set", body = LoginResponse),
(status = 401, description = "Unauthorized")
),
tag = "Auth"
)]
pub async fn login_cookie(
State(state): State<AppState>,
jar: CookieJar,
Json(payload): Json<LoginRequest>,
) -> Result<(CookieJar, Json<LoginResponse>), HTTPError> {
let user = state
.repositories
.user
.check_password(&payload.username, &payload.password)
.await
.map_err(|_| HTTPError::Unauthorized)?;
let token = create_jwt(
user.id,
&user.username,
user.is_superuser,
&state.config.jwt.secret,
state.config.jwt.duration,
)
.map_err(|_| HTTPError::InternalServerError("Failed to create JWT token".to_string()))?;
// Création du cookie sécurisé contenant le token JWT
let cookie = Cookie::build(("token", token.clone()))
.path("/")
.http_only(true)
.same_site(SameSite::Lax)
.secure(false) // Mettez à true si vous forcez le HTTPS en production
.build();
let updated_jar = jar.add(cookie);
Ok((updated_jar, Json(LoginResponse { token })))
}
#[utoipa::path(
post,
path = "/auth/logout",
responses(
(status = 200, description = "Logout successful")
),
tag = "Auth"
)]
pub async fn logout_cookie(jar: CookieJar) -> Result<CookieJar, HTTPError> {
// On crée un cookie expiré en lui donnant une durée négative de 1 seconde (ou Duration::ZERO)
let cookie = Cookie::build(("token", ""))
.path("/")
.max_age(time::Duration::seconds(-1))
.build();
let updated_jar = jar.add(cookie);
Ok(updated_jar)
}
#[utoipa::path(
post,
path = "/auth/me",
+2 -1
View File
@@ -5,6 +5,7 @@ use axum::Router;
pub fn router() -> OxRouter {
Router::new()
.route("/auth/login", post(handlers::login_user_pw))
.route("/auth/login", post(handlers::login_cookie))
.route("/auth/bearer-login", post(handlers::login_bearer))
.route("/auth/me", get(handlers::me))
}
+6 -3
View File
@@ -13,7 +13,8 @@ pub struct CreateChannelRequest {
pub channel_type: ChannelType,
#[schema(example = "général")]
pub name: Option<String>,
pub default_permissions: Option<u64>,
pub default_channel_permissions: Option<u64>,
pub default_voice_permissions: Option<u64>,
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]
@@ -23,7 +24,8 @@ pub struct UpdateChannelRequest {
pub position: i32,
pub channel_type: ChannelType,
pub name: Option<String>,
pub default_permissions: Option<u64>,
pub default_channel_permissions: Option<u64>,
pub default_voice_permissions: Option<u64>,
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]
@@ -36,5 +38,6 @@ pub struct ChannelResponse {
pub name: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub default_permissions: Option<u64>,
pub default_channel_permissions: Option<u64>,
pub default_voice_permissions: Option<u64>,
}
+6 -3
View File
@@ -13,7 +13,8 @@ pub fn channel_model_to_channel_response(model: channel::Model) -> ChannelRespon
name: model.name,
created_at: model.created_at,
updated_at: model.updated_at,
default_permissions: model.default_permissions,
default_channel_permissions: model.default_channel_permissions.map(|p| p as u64),
default_voice_permissions: model.default_voice_permissions.map(|p| p as u64),
}
}
@@ -25,7 +26,8 @@ pub fn create_request_to_am(req: CreateChannelRequest) -> channel::ActiveModel {
position: Set(req.position),
channel_type: Set(req.channel_type),
name: Set(req.name),
default_permissions: Set(req.default_permissions),
default_channel_permissions: Set(req.default_channel_permissions.map(|p| p as i64)),
default_voice_permissions: Set(req.default_voice_permissions.map(|p| p as i64)),
..Default::default()
}
}
@@ -38,7 +40,8 @@ pub fn update_request_to_am(id: Uuid, req: UpdateChannelRequest) -> channel::Act
position: Set(req.position),
channel_type: Set(req.channel_type),
name: Set(req.name),
default_permissions: Set(req.default_permissions),
default_channel_permissions: Set(req.default_channel_permissions.map(|p| p as i64)),
default_voice_permissions: Set(req.default_voice_permissions.map(|p| p as i64)),
..Default::default()
}
}
+1 -2
View File
@@ -4,7 +4,7 @@ use crate::models::user::Model as User;
use crate::routes::gateway::GatewayClient;
use axum::{
extract::{
ws::{Message, WebSocket, WebSocketUpgrade}, Query,
ws::{Message, WebSocket, WebSocketUpgrade},
State,
},
response::IntoResponse,
@@ -19,7 +19,6 @@ pub struct WsQuery {
}
pub async fn ws_handler(
Query(query): Query<WsQuery>,
ws: WebSocketUpgrade,
State(state): State<AppState>,
CurrentUser(user): CurrentUser,
+3 -1
View File
@@ -6,7 +6,9 @@ use utoipa::{Modify, OpenApi};
#[derive(OpenApi)]
#[openapi(
paths(
auth::handlers::login_user_pw,
auth::handlers::login_bearer,
auth::handlers::login_cookie,
auth::handlers::logout_cookie,
auth::handlers::me,
user::handlers::get_all,
user::handlers::get_by_id,