diff --git a/crates/frontend/src/config.rs b/crates/frontend/src/config.rs index d378cde..f709c82 100644 --- a/crates/frontend/src/config.rs +++ b/crates/frontend/src/config.rs @@ -6,6 +6,7 @@ fn default_true() -> bool { } #[derive(Deserialize, Serialize, Clone)] +#[serde(deny_unknown_fields)] pub struct OidcConfig { pub name: String, pub issuer: IssuerUrl, @@ -13,6 +14,7 @@ pub struct OidcConfig { pub client_secret: Option, pub scopes: Vec, pub allow_sign_up: bool, + pub require_group: Option, } #[derive(Deserialize, Serialize, Clone)] diff --git a/crates/frontend/src/oidc/mod.rs b/crates/frontend/src/oidc/mod.rs index 5a361b8..a92dca6 100644 --- a/crates/frontend/src/oidc/mod.rs +++ b/crates/frontend/src/oidc/mod.rs @@ -32,6 +32,14 @@ struct OidcState { redirect_uri: Option, } +#[derive(Debug, Deserialize, Serialize)] +struct GroupAdditionalClaims { + #[serde(default)] + pub groups: Vec, +} + +impl openidconnect::AdditionalClaims for GroupAdditionalClaims {} + fn get_http_client() -> reqwest::Client { reqwest::ClientBuilder::new() // Following redirects opens the client up to SSRF vulnerabilities. @@ -166,7 +174,7 @@ pub async fn route_get_oidc_callback( .ok_or(OidcError::Other("OIDC provider did not return an ID token"))? .claims(&oidc_client.id_token_verifier(), &oidc_state.nonce)?; - let user_info_claims: UserInfoClaims = oidc_client + let user_info_claims: UserInfoClaims = oidc_client .user_info( token_response.access_token().clone(), Some(id_claims.subject().clone()), @@ -175,6 +183,17 @@ pub async fn route_get_oidc_callback( .await .map_err(|_| OidcError::Other("Error fetching user info"))?; + if let Some(require_group) = oidc_config.require_group { + if !user_info_claims + .additional_claims() + .groups + .contains(&require_group) + { + return Ok(HttpResponse::build(StatusCode::UNAUTHORIZED) + .body("User is not in an authorized group to use RustiCal")); + } + } + let user_id = user_info_claims .preferred_username() .ok_or(OidcError::Other("Missing preferred_username claim"))?