feat(auth): add OpenID Connect (OIDC) authentication support

Implements OIDC Authorization Code Flow for external identity providers
(Authentik, Keycloak, etc.) with JIT user provisioning.

New features:
- OidcService with OpenID Discovery, JWKS caching, RS256 ID token validation
- Authorization Code Flow: /api/auth/oidc/authorize -> IdP -> /api/auth/oidc/callback
- JIT user provisioning from OIDC claims (sub, email, name, groups)
- OIDC group-to-role mapping (admin_groups config)
- Provider info endpoint: GET /api/auth/oidc/providers
- Option to disable password login entirely (OXICLOUD_OIDC_DISABLE_PASSWORD_LOGIN)
- Auto-provision toggle (OXICLOUD_OIDC_AUTO_PROVISION)
- Email collision detection (security: prevents account takeover)

Configuration (env vars):
- OXICLOUD_OIDC_ENABLED, OXICLOUD_OIDC_ISSUER_URL
- OXICLOUD_OIDC_CLIENT_ID, OXICLOUD_OIDC_CLIENT_SECRET
- OXICLOUD_OIDC_REDIRECT_URI, OXICLOUD_OIDC_SCOPES
- OXICLOUD_OIDC_FRONTEND_URL, OXICLOUD_OIDC_PROVIDER_NAME
- OXICLOUD_OIDC_AUTO_PROVISION, OXICLOUD_OIDC_ADMIN_GROUPS
- OXICLOUD_OIDC_DISABLE_PASSWORD_LOGIN

DB migration:
- ALTER TABLE auth.users ADD oidc_provider, oidc_subject columns
- UNIQUE index on (oidc_provider, oidc_subject)

Files changed: 14 files, ~1400 lines added
Dependencies: reqwest 0.12 (rustls-tls-webpki-roots), base64 0.22
This commit is contained in:
Dionisio
2026-02-10 20:32:32 +01:00
parent 65f4e10236
commit 8ef62109a3
14 changed files with 1407 additions and 33 deletions
+14
View File
@@ -8,6 +8,7 @@ use crate::application::services::folder_service::FolderService;
use crate::infrastructure::repositories::{UserPgRepository, SessionPgRepository};
use crate::infrastructure::services::password_hasher::Argon2PasswordHasher;
use crate::infrastructure::services::jwt_service::JwtTokenService;
use crate::infrastructure::services::oidc_service::OidcService;
use crate::common::config::AppConfig;
use crate::common::di::AuthServices;
@@ -42,6 +43,19 @@ pub async fn create_auth_services(
if let Some(folder_svc) = folder_service {
auth_app_service = auth_app_service.with_folder_service(folder_svc);
}
// Configure OIDC service if enabled
if config.oidc.enabled {
tracing::info!("Initializing OIDC service (provider: {}, issuer: {})",
config.oidc.provider_name, config.oidc.issuer_url);
let oidc_service = Arc::new(OidcService::new(config.oidc.clone()));
auth_app_service = auth_app_service.with_oidc(oidc_service, config.oidc.clone());
if config.oidc.disable_password_login {
tracing::warn!("Password login is DISABLED — only OIDC authentication is allowed");
}
}
// Empaquetar servicio en Arc
let auth_application_service = Arc::new(auth_app_service);