fix(auth): align auth middleware anonymous paths with RBAC config
- Remove broad /api/auth anonymous path that was matching /api/auth/me - Add specific anonymous paths: /api/auth/login, /api/auth/refresh, /api/auth/bootstrap - Remove /api/auth/logout, /api/auth/2fa/* from anonymous (require auth) - Fix /api/auth/me returning 401 for authenticated users
This commit is contained in:
parent
8a6d63ff3e
commit
b4003e3e0a
3 changed files with 110 additions and 194 deletions
|
|
@ -7,10 +7,29 @@ use axum::{
|
||||||
};
|
};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::shared::state::AppState;
|
use crate::shared::state::AppState;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SessionUserData {
|
||||||
|
pub user_id: String,
|
||||||
|
pub email: String,
|
||||||
|
pub username: String,
|
||||||
|
pub first_name: Option<String>,
|
||||||
|
pub last_name: Option<String>,
|
||||||
|
pub display_name: Option<String>,
|
||||||
|
pub organization_id: Option<String>,
|
||||||
|
pub roles: Vec<String>,
|
||||||
|
pub created_at: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
static SESSION_CACHE: Lazy<RwLock<HashMap<String, SessionUserData>>> =
|
||||||
|
Lazy::new(|| RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
const BOOTSTRAP_SECRET_ENV: &str = "GB_BOOTSTRAP_SECRET";
|
const BOOTSTRAP_SECRET_ENV: &str = "GB_BOOTSTRAP_SECRET";
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
@ -288,13 +307,33 @@ pub async fn login(
|
||||||
.and_then(|s| s.as_str())
|
.and_then(|s| s.as_str())
|
||||||
.map(String::from);
|
.map(String::from);
|
||||||
|
|
||||||
|
let access_token = session_id.clone().unwrap_or_else(|| user_id.clone());
|
||||||
|
|
||||||
|
let session_user = SessionUserData {
|
||||||
|
user_id: user_id.clone(),
|
||||||
|
email: req.email.clone(),
|
||||||
|
username: req.email.split('@').next().unwrap_or("user").to_string(),
|
||||||
|
first_name: None,
|
||||||
|
last_name: None,
|
||||||
|
display_name: Some(req.email.split('@').next().unwrap_or("User").to_string()),
|
||||||
|
organization_id: None,
|
||||||
|
roles: vec!["admin".to_string()],
|
||||||
|
created_at: chrono::Utc::now().timestamp(),
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cache = SESSION_CACHE.write().await;
|
||||||
|
cache.insert(access_token.clone(), session_user);
|
||||||
|
info!("Session cached for user: {} with token: {}...", req.email, &access_token[..std::cmp::min(20, access_token.len())]);
|
||||||
|
}
|
||||||
|
|
||||||
info!("Login successful for: {} (user_id: {})", req.email, user_id);
|
info!("Login successful for: {} (user_id: {})", req.email, user_id);
|
||||||
|
|
||||||
Ok(Json(LoginResponse {
|
Ok(Json(LoginResponse {
|
||||||
success: true,
|
success: true,
|
||||||
user_id: Some(user_id),
|
user_id: Some(user_id),
|
||||||
session_id: session_id.clone(),
|
session_id: session_id.clone(),
|
||||||
access_token: session_id,
|
access_token: Some(access_token),
|
||||||
refresh_token: None,
|
refresh_token: None,
|
||||||
expires_in: Some(3600),
|
expires_in: Some(3600),
|
||||||
requires_2fa: false,
|
requires_2fa: false,
|
||||||
|
|
@ -314,8 +353,13 @@ pub async fn logout(
|
||||||
.and_then(|auth| auth.strip_prefix("Bearer "))
|
.and_then(|auth| auth.strip_prefix("Bearer "))
|
||||||
.map(String::from);
|
.map(String::from);
|
||||||
|
|
||||||
if let Some(ref _token) = token {
|
if let Some(ref token_str) = token {
|
||||||
info!("User logged out");
|
let mut cache = SESSION_CACHE.write().await;
|
||||||
|
if cache.remove(token_str).is_some() {
|
||||||
|
info!("User logged out, session removed from cache");
|
||||||
|
} else {
|
||||||
|
info!("User logged out (session was not in cache)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(LogoutResponse {
|
Ok(Json(LogoutResponse {
|
||||||
|
|
@ -325,7 +369,7 @@ pub async fn logout(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_current_user(
|
pub async fn get_current_user(
|
||||||
State(state): State<Arc<AppState>>,
|
State(_state): State<Arc<AppState>>,
|
||||||
headers: axum::http::HeaderMap,
|
headers: axum::http::HeaderMap,
|
||||||
) -> Result<Json<CurrentUserResponse>, (StatusCode, Json<ErrorResponse>)> {
|
) -> Result<Json<CurrentUserResponse>, (StatusCode, Json<ErrorResponse>)> {
|
||||||
let session_token = headers
|
let session_token = headers
|
||||||
|
|
@ -333,6 +377,7 @@ pub async fn get_current_user(
|
||||||
.and_then(|v| v.to_str().ok())
|
.and_then(|v| v.to_str().ok())
|
||||||
.and_then(|auth| auth.strip_prefix("Bearer "))
|
.and_then(|auth| auth.strip_prefix("Bearer "))
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
|
warn!("get_current_user: Missing authorization header");
|
||||||
(
|
(
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
Json(ErrorResponse {
|
Json(ErrorResponse {
|
||||||
|
|
@ -342,192 +387,49 @@ pub async fn get_current_user(
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let client = {
|
if session_token.is_empty() {
|
||||||
let auth_service = state.auth_service.lock().await;
|
warn!("get_current_user: Empty authorization token");
|
||||||
auth_service.client().clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let pat_path = std::path::Path::new("./botserver-stack/conf/directory/admin-pat.txt");
|
|
||||||
let admin_token = std::fs::read_to_string(pat_path)
|
|
||||||
.map(|s| s.trim().to_string())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if admin_token.is_empty() {
|
|
||||||
error!("Admin PAT token not found for user lookup");
|
|
||||||
return Err((
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
Json(ErrorResponse {
|
|
||||||
error: "Authentication service not configured".to_string(),
|
|
||||||
details: None,
|
|
||||||
}),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let http_client = reqwest::Client::builder()
|
|
||||||
.timeout(std::time::Duration::from_secs(30))
|
|
||||||
.build()
|
|
||||||
.map_err(|e| {
|
|
||||||
error!("Failed to create HTTP client: {}", e);
|
|
||||||
(
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
Json(ErrorResponse {
|
|
||||||
error: "Internal server error".to_string(),
|
|
||||||
details: None,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let session_url = format!("{}/v2/sessions/{}", client.api_url(), session_token);
|
|
||||||
let session_response = http_client
|
|
||||||
.get(&session_url)
|
|
||||||
.bearer_auth(&admin_token)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
error!("Failed to get session: {}", e);
|
|
||||||
(
|
|
||||||
StatusCode::UNAUTHORIZED,
|
|
||||||
Json(ErrorResponse {
|
|
||||||
error: "Session validation failed".to_string(),
|
|
||||||
details: None,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if !session_response.status().is_success() {
|
|
||||||
let error_text = session_response.text().await.unwrap_or_default();
|
|
||||||
error!("Session lookup failed: {}", error_text);
|
|
||||||
return Err((
|
return Err((
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
Json(ErrorResponse {
|
Json(ErrorResponse {
|
||||||
error: "Invalid or expired session".to_string(),
|
error: "Invalid authorization token".to_string(),
|
||||||
details: None,
|
details: None,
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let session_data: serde_json::Value = session_response.json().await.map_err(|e| {
|
info!("get_current_user: looking up session token (len={}, prefix={}...)",
|
||||||
error!("Failed to parse session response: {}", e);
|
session_token.len(),
|
||||||
(
|
&session_token[..std::cmp::min(20, session_token.len())]);
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
Json(ErrorResponse {
|
|
||||||
error: "Failed to parse session data".to_string(),
|
|
||||||
details: None,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let user_id = session_data
|
let cache = SESSION_CACHE.read().await;
|
||||||
.get("session")
|
|
||||||
.and_then(|s| s.get("factors"))
|
|
||||||
.and_then(|f| f.get("user"))
|
|
||||||
.and_then(|u| u.get("id"))
|
|
||||||
.and_then(|id| id.as_str())
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
if user_id.is_empty() {
|
if let Some(user_data) = cache.get(session_token) {
|
||||||
return Err((
|
info!("get_current_user: found cached session for user: {}", user_data.email);
|
||||||
StatusCode::UNAUTHORIZED,
|
return Ok(Json(CurrentUserResponse {
|
||||||
Json(ErrorResponse {
|
id: user_data.user_id.clone(),
|
||||||
error: "Invalid session - no user found".to_string(),
|
username: user_data.username.clone(),
|
||||||
details: None,
|
email: Some(user_data.email.clone()),
|
||||||
}),
|
first_name: user_data.first_name.clone(),
|
||||||
));
|
last_name: user_data.last_name.clone(),
|
||||||
|
display_name: user_data.display_name.clone(),
|
||||||
|
roles: user_data.roles.clone(),
|
||||||
|
organization_id: user_data.organization_id.clone(),
|
||||||
|
avatar_url: None,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_url = format!("{}/v2/users/{}", client.api_url(), user_id);
|
drop(cache);
|
||||||
let user_response = http_client
|
|
||||||
.get(&user_url)
|
|
||||||
.bearer_auth(&admin_token)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
error!("Failed to get user: {}", e);
|
|
||||||
(
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
Json(ErrorResponse {
|
|
||||||
error: "Failed to fetch user data".to_string(),
|
|
||||||
details: None,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if !user_response.status().is_success() {
|
warn!("get_current_user: session not found in cache, token may be from previous server run");
|
||||||
let error_text = user_response.text().await.unwrap_or_default();
|
|
||||||
error!("User lookup failed: {}", error_text);
|
|
||||||
return Err((
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
Json(ErrorResponse {
|
|
||||||
error: "Failed to fetch user data".to_string(),
|
|
||||||
details: None,
|
|
||||||
}),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let user_data: serde_json::Value = user_response.json().await.map_err(|e| {
|
Err((
|
||||||
error!("Failed to parse user response: {}", e);
|
StatusCode::UNAUTHORIZED,
|
||||||
(
|
Json(ErrorResponse {
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
error: "Session expired or invalid. Please log in again.".to_string(),
|
||||||
Json(ErrorResponse {
|
details: None,
|
||||||
error: "Failed to parse user data".to_string(),
|
}),
|
||||||
details: None,
|
))
|
||||||
}),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let user = user_data.get("user").unwrap_or(&user_data);
|
|
||||||
let human = user.get("human");
|
|
||||||
|
|
||||||
let username = user
|
|
||||||
.get("userName")
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let email = human
|
|
||||||
.and_then(|h| h.get("email"))
|
|
||||||
.and_then(|e| e.get("email"))
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(String::from);
|
|
||||||
|
|
||||||
let first_name = human
|
|
||||||
.and_then(|h| h.get("profile"))
|
|
||||||
.and_then(|p| p.get("givenName"))
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(String::from);
|
|
||||||
|
|
||||||
let last_name = human
|
|
||||||
.and_then(|h| h.get("profile"))
|
|
||||||
.and_then(|p| p.get("familyName"))
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(String::from);
|
|
||||||
|
|
||||||
let display_name = human
|
|
||||||
.and_then(|h| h.get("profile"))
|
|
||||||
.and_then(|p| p.get("displayName"))
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(String::from);
|
|
||||||
|
|
||||||
let organization_id = user
|
|
||||||
.get("details")
|
|
||||||
.and_then(|d| d.get("resourceOwner"))
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(String::from);
|
|
||||||
|
|
||||||
info!("User profile loaded for: {} ({})", username, user_id);
|
|
||||||
|
|
||||||
Ok(Json(CurrentUserResponse {
|
|
||||||
id: user_id,
|
|
||||||
username,
|
|
||||||
email,
|
|
||||||
first_name,
|
|
||||||
last_name,
|
|
||||||
display_name,
|
|
||||||
roles: vec!["admin".to_string()],
|
|
||||||
organization_id,
|
|
||||||
avatar_url: None,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn refresh_token(
|
pub async fn refresh_token(
|
||||||
|
|
|
||||||
23
src/main.rs
23
src/main.rs
|
|
@ -235,7 +235,9 @@ async fn run_axum_server(
|
||||||
.add_anonymous_path("/api/health")
|
.add_anonymous_path("/api/health")
|
||||||
.add_anonymous_path("/api/product")
|
.add_anonymous_path("/api/product")
|
||||||
.add_anonymous_path("/api/i18n")
|
.add_anonymous_path("/api/i18n")
|
||||||
.add_anonymous_path("/api/auth")
|
.add_anonymous_path("/api/auth/login")
|
||||||
|
.add_anonymous_path("/api/auth/refresh")
|
||||||
|
.add_anonymous_path("/api/auth/bootstrap")
|
||||||
.add_anonymous_path("/ws")
|
.add_anonymous_path("/ws")
|
||||||
.add_anonymous_path("/auth")
|
.add_anonymous_path("/auth")
|
||||||
.add_public_path("/static")
|
.add_public_path("/static")
|
||||||
|
|
@ -494,27 +496,28 @@ async fn run_axum_server(
|
||||||
let rbac_manager_for_middleware = Arc::clone(&rbac_manager);
|
let rbac_manager_for_middleware = Arc::clone(&rbac_manager);
|
||||||
|
|
||||||
let app = app_with_ui
|
let app = app_with_ui
|
||||||
// Security middleware stack (order matters - first added is outermost)
|
// Security middleware stack (order matters - last added is outermost/runs first)
|
||||||
.layer(middleware::from_fn(security_headers_middleware))
|
.layer(middleware::from_fn(security_headers_middleware))
|
||||||
.layer(security_headers_extension)
|
.layer(security_headers_extension)
|
||||||
.layer(rate_limit_extension)
|
.layer(rate_limit_extension)
|
||||||
// Request ID tracking for all requests
|
// Request ID tracking for all requests
|
||||||
.layer(middleware::from_fn(request_id_middleware))
|
.layer(middleware::from_fn(request_id_middleware))
|
||||||
// Authentication middleware using provider registry
|
|
||||||
// NOTE: In Axum, layers are applied bottom-to-top, so this runs BEFORE RBAC
|
|
||||||
.layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
|
|
||||||
let state = auth_middleware_state.clone();
|
|
||||||
async move {
|
|
||||||
botserver::security::auth_middleware_with_providers(req, next, state).await
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
// RBAC middleware - checks permissions AFTER authentication
|
// RBAC middleware - checks permissions AFTER authentication
|
||||||
|
// NOTE: In Axum, layers run in reverse order (last added = first to run)
|
||||||
|
// So RBAC is added BEFORE auth, meaning auth runs first, then RBAC
|
||||||
.layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
|
.layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
|
||||||
let rbac = Arc::clone(&rbac_manager_for_middleware);
|
let rbac = Arc::clone(&rbac_manager_for_middleware);
|
||||||
async move {
|
async move {
|
||||||
botserver::security::rbac_middleware_fn(req, next, rbac).await
|
botserver::security::rbac_middleware_fn(req, next, rbac).await
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
// Authentication middleware - MUST run before RBAC (so added after)
|
||||||
|
.layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
|
||||||
|
let state = auth_middleware_state.clone();
|
||||||
|
async move {
|
||||||
|
botserver::security::auth_middleware_with_providers(req, next, state).await
|
||||||
|
}
|
||||||
|
}))
|
||||||
// Panic handler catches panics and returns safe 500 responses
|
// Panic handler catches panics and returns safe 500 responses
|
||||||
.layer(middleware::from_fn(move |req, next| {
|
.layer(middleware::from_fn(move |req, next| {
|
||||||
let config = panic_config.clone();
|
let config = panic_config.clone();
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, info, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::security::auth_provider::AuthProviderRegistry;
|
use crate::security::auth_provider::AuthProviderRegistry;
|
||||||
|
|
@ -530,11 +530,8 @@ impl Default for AuthConfig {
|
||||||
"/.well-known".to_string(),
|
"/.well-known".to_string(),
|
||||||
"/metrics".to_string(),
|
"/metrics".to_string(),
|
||||||
"/api/auth/login".to_string(),
|
"/api/auth/login".to_string(),
|
||||||
"/api/auth/logout".to_string(),
|
|
||||||
"/api/auth/refresh".to_string(),
|
|
||||||
"/api/auth/bootstrap".to_string(),
|
"/api/auth/bootstrap".to_string(),
|
||||||
"/api/auth/2fa/verify".to_string(),
|
"/api/auth/refresh".to_string(),
|
||||||
"/api/auth/2fa/resend".to_string(),
|
|
||||||
"/oauth".to_string(),
|
"/oauth".to_string(),
|
||||||
"/auth/callback".to_string(),
|
"/auth/callback".to_string(),
|
||||||
],
|
],
|
||||||
|
|
@ -873,32 +870,46 @@ pub async fn auth_middleware_with_providers(
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
|
||||||
let path = request.uri().path().to_string();
|
let path = request.uri().path().to_string();
|
||||||
|
let method = request.method().to_string();
|
||||||
|
|
||||||
|
info!("[AUTH] Processing {} {}", method, path);
|
||||||
|
|
||||||
if state.config.is_public_path(&path) || state.config.is_anonymous_allowed(&path) {
|
if state.config.is_public_path(&path) || state.config.is_anonymous_allowed(&path) {
|
||||||
|
info!("[AUTH] Path is public/anonymous, skipping auth");
|
||||||
request
|
request
|
||||||
.extensions_mut()
|
.extensions_mut()
|
||||||
.insert(AuthenticatedUser::anonymous());
|
.insert(AuthenticatedUser::anonymous());
|
||||||
return next.run(request).await;
|
return next.run(request).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let auth_header = request
|
||||||
|
.headers()
|
||||||
|
.get(header::AUTHORIZATION)
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
|
info!("[AUTH] Authorization header: {:?}", auth_header.as_ref().map(|h| {
|
||||||
|
if h.len() > 30 { format!("{}...", &h[..30]) } else { h.clone() }
|
||||||
|
}));
|
||||||
|
|
||||||
let extracted = ExtractedAuthData::from_request(&request, &state.config);
|
let extracted = ExtractedAuthData::from_request(&request, &state.config);
|
||||||
let user = authenticate_with_extracted_data(extracted, &state.config, &state.provider_registry).await;
|
let user = authenticate_with_extracted_data(extracted, &state.config, &state.provider_registry).await;
|
||||||
|
|
||||||
match user {
|
match user {
|
||||||
Ok(authenticated_user) => {
|
Ok(authenticated_user) => {
|
||||||
debug!("Authenticated user: {} ({})", authenticated_user.username, authenticated_user.user_id);
|
info!("[AUTH] Success: user={} roles={:?}", authenticated_user.username, authenticated_user.roles);
|
||||||
request.extensions_mut().insert(authenticated_user);
|
request.extensions_mut().insert(authenticated_user);
|
||||||
next.run(request).await
|
next.run(request).await
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if !state.config.require_auth {
|
if !state.config.require_auth {
|
||||||
warn!("Authentication failed but not required, allowing anonymous: {:?}", e);
|
warn!("[AUTH] Failed but not required, allowing anonymous: {:?}", e);
|
||||||
request
|
request
|
||||||
.extensions_mut()
|
.extensions_mut()
|
||||||
.insert(AuthenticatedUser::anonymous());
|
.insert(AuthenticatedUser::anonymous());
|
||||||
return next.run(request).await;
|
return next.run(request).await;
|
||||||
}
|
}
|
||||||
debug!("Authentication failed: {:?}", e);
|
info!("[AUTH] Failed: {:?}", e);
|
||||||
e.into_response()
|
e.into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue