mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 16:00:38 +00:00
[ee] feat(scim): OAuth2 client-credentials grant for SCIM provisioning
Support OAuth 2.0 client-credentials authentication for SCIM provisioning (e.g. Microsoft Entra ID) alongside the existing static bearer token, so identity providers can use short-lived, rotatable access tokens. Backend (EE companion PR modifies scim_ee.rs): - Unauthenticated token endpoint POST /api/scim_token/token issues a short-lived scope:scim JWT via the client-credentials grant. - has_scim_token validates SCIM JWTs (when OAuth is configured) in addition to the static token — fully backward compatible. - Super-admin config endpoints (generate/rotate/disable) store the client secret hashed (SHA-256). New scim_oauth global setting is agent-worker blocked and live-reloaded. Frontend: - OAuth 2.0 client-credentials section in the SCIM/SAML instance settings: enable toggle, generate-secret (shown once), copyable token endpoint and client ID, regenerate/disable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -203,6 +203,10 @@ lazy_static::lazy_static! {
|
||||
pub static ref REQUEST_SIZE_LIMIT: Arc<RwLock<usize>> = Arc::new(RwLock::new(DEFAULT_BODY_LIMIT));
|
||||
|
||||
pub static ref SCIM_TOKEN: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
// Raw JSON of the SCIM OAuth2 client-credentials config (client_id +
|
||||
// hashed client_secret + optional token TTL). Parsed lazily at the token
|
||||
// endpoint; `None` disables the OAuth grant entirely (see has_scim_token).
|
||||
pub static ref SCIM_OAUTH_CONFIG: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref SAML_METADATA: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
|
||||
|
||||
@@ -748,6 +752,10 @@ pub async fn run_server(
|
||||
scim_oss::global_service()
|
||||
.route_layer(axum::middleware::from_fn(has_scim_token)),
|
||||
)
|
||||
// Unauthenticated: the OAuth2 client-credentials token endpoint
|
||||
// authenticates itself via client_id/client_secret, so it must
|
||||
// NOT sit behind has_scim_token.
|
||||
.nest("/scim_token", scim_oss::token_service())
|
||||
.nest("/tokens", token::global_service())
|
||||
.nest("/concurrency_groups", concurrency_groups::global_service())
|
||||
.nest("/scripts_u", scripts::global_unauthed_service())
|
||||
|
||||
@@ -18,6 +18,11 @@ pub fn global_service() -> Router {
|
||||
Router::new().route("/ee", get(ee))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
pub fn token_service() -> Router {
|
||||
Router::new().route("/ee", get(ee))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
pub async fn ee() -> String {
|
||||
return "Enterprise Edition".to_string();
|
||||
|
||||
Reference in New Issue
Block a user