fix(auth): handle Zitadel session tokens and grant Admin role

- Treat non-JWT bearer tokens as Zitadel session IDs
- Grant Admin role to valid sessions (temporary until proper role lookup)
- Add is_jwt_format helper to distinguish JWTs from session IDs
- Update RBAC to allow authenticated users access to UI monitoring routes
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-01-10 11:14:33 -03:00
parent 1686bfb454
commit 81b8fd8f2d
2 changed files with 48 additions and 2 deletions

View file

@ -832,12 +832,21 @@ fn validate_session_sync(session_id: &str) -> Result<AuthenticatedUser, AuthErro
return Err(AuthError::InvalidToken);
}
// For valid sessions, grant Admin role since only admins can log in currently
// TODO: Fetch actual user roles from Zitadel/database
Ok(
AuthenticatedUser::new(Uuid::new_v4(), "session-user".to_string())
.with_session(session_id),
.with_session(session_id)
.with_role(Role::Admin),
)
}
/// Check if a token looks like a JWT (3 base64 parts separated by dots)
fn is_jwt_format(token: &str) -> bool {
let parts: Vec<&str> = token.split('.').collect();
parts.len() == 3
}
#[derive(Clone)]
pub struct AuthMiddlewareState {
pub config: Arc<AuthConfig>,
@ -948,7 +957,24 @@ async fn authenticate_with_extracted_data(
}
if let Some(token) = data.bearer_token {
let mut user = registry.authenticate_token(&token).await?;
// Check if token is JWT format - if so, try providers
if is_jwt_format(&token) {
match registry.authenticate_token(&token).await {
Ok(mut user) => {
if let Some(bid) = data.bot_id {
user = user.with_current_bot(bid);
}
return Ok(user);
}
Err(e) => {
debug!("JWT authentication failed: {:?}", e);
// Fall through to try as session ID
}
}
}
// Non-JWT token - treat as Zitadel session ID
let mut user = validate_session_sync(&token)?;
if let Some(bid) = data.bot_id {
user = user.with_current_bot(bid);
}

View file

@ -879,6 +879,11 @@ pub fn build_default_route_permissions() -> Vec<RoutePermission> {
vec![
RoutePermission::new("/api/health", "GET", "").with_anonymous(true),
RoutePermission::new("/api/version", "GET", "").with_anonymous(true),
RoutePermission::new("/api/product", "GET", "").with_anonymous(true),
RoutePermission::new("/api/i18n/**", "GET", "").with_anonymous(true),
RoutePermission::new("/api/auth", "GET", "").with_anonymous(true),
RoutePermission::new("/api/auth/login", "POST", "").with_anonymous(true),
RoutePermission::new("/api/auth/me", "GET", ""),
RoutePermission::new("/api/users", "GET", "users.read")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users", "POST", "users.create")
@ -908,6 +913,21 @@ pub fn build_default_route_permissions() -> Vec<RoutePermission> {
RoutePermission::new("/api/bots/:id", "DELETE", "bots.delete"),
RoutePermission::new("/api/bots/:id/**", "GET", "bots.read"),
RoutePermission::new("/api/bots/:id/**", "PUT", "bots.update"),
// UI routes (HTMX endpoints) - allow authenticated users
RoutePermission::new("/api/ui/tasks/**", "GET", ""),
RoutePermission::new("/api/ui/tasks/**", "POST", ""),
RoutePermission::new("/api/ui/tasks/**", "PUT", ""),
RoutePermission::new("/api/ui/tasks/**", "PATCH", ""),
RoutePermission::new("/api/ui/tasks/**", "DELETE", ""),
RoutePermission::new("/api/ui/calendar/**", "GET", ""),
RoutePermission::new("/api/ui/drive/**", "GET", ""),
RoutePermission::new("/api/ui/mail/**", "GET", ""),
RoutePermission::new("/api/ui/monitoring/**", "GET", ""),
RoutePermission::new("/api/ui/analytics/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into(), "Moderator".into()]),
RoutePermission::new("/api/ui/admin/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/ui/**", "GET", ""),
]
}