diff --git a/conduwuit-example.toml b/conduwuit-example.toml index cb3f87a32..a98a35639 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -2057,6 +2057,13 @@ # #client_secret = +# A path to a file which Continuwuity will read the client secret from. +# If this option is set, it will override `client_secret`. +# +# The server will fail to start if the file cannot be read. +# +#client_secret_file = + # Additional scopes Continuwuity should request from the IDP. This may be # necessary to access certain claims. Continuwuity always requests the # `openid` scope. diff --git a/src/core/config/check.rs b/src/core/config/check.rs index bc9878598..840413823 100644 --- a/src/core/config/check.rs +++ b/src/core/config/check.rs @@ -289,6 +289,15 @@ pub fn check(config: &Config) -> Result { | _ => (), } + if let Some(oidc) = &config.oauth.oidc + && oidc.client_secret.is_none() + && oidc.client_secret_file.is_none() + { + return Err!( + "Either `client_secret` or `client_secret_file` must be set if OIDC is configured." + ); + } + Ok(()) } diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 7fe35c93f..e56dcbf0b 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -2484,7 +2484,13 @@ pub struct OidcConfig { /// The OAuth client secret for Continuwuity to use when communicating with /// the identity provider. - pub client_secret: ClientSecret, + pub client_secret: Option, + + /// A path to a file which Continuwuity will read the client secret from. + /// If this option is set, it will override `client_secret`. + /// + /// The server will fail to start if the file cannot be read. + pub client_secret_file: Option, /// Additional scopes Continuwuity should request from the IDP. This may be /// necessary to access certain claims. Continuwuity always requests the diff --git a/src/service/oidc/mod.rs b/src/service/oidc/mod.rs index a1b100dc7..507cbd828 100644 --- a/src/service/oidc/mod.rs +++ b/src/service/oidc/mod.rs @@ -9,9 +9,9 @@ use conduwuit::{ use database::{Deserialized, Map}; use lettre::Address; use openidconnect::{ - AdditionalClaims, AuthorizationCode, CsrfToken, EmptyExtraTokenFields, EndpointMaybeSet, - EndpointNotSet, EndpointSet, IdTokenClaims, IdTokenFields, IssuerUrl, Nonce, - PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, StandardErrorResponse, + AdditionalClaims, AuthorizationCode, ClientSecret, CsrfToken, EmptyExtraTokenFields, + EndpointMaybeSet, EndpointNotSet, EndpointSet, IdTokenClaims, IdTokenFields, IssuerUrl, + Nonce, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, StandardErrorResponse, StandardTokenResponse, TokenResponse, core::{ CoreAuthDisplay, CoreAuthPrompt, CoreAuthenticationFlow, CoreErrorResponseType, @@ -59,6 +59,7 @@ struct Services { struct OidcClient { config: OidcConfig, + client_secret: ClientSecret, machine: SetOnce, client: reqwest::Client, } @@ -130,8 +131,19 @@ impl crate::Service for Service { openidsubject_localpart: args.db["openidsubject_localpart"].clone(), openidsubject_currentpictureurl: args.db["openidsubject_currentpictureurl"].clone(), }, - client: args.server.config.oauth.oidc.as_ref().map(|config| OidcClient { + client: args.server.config.oauth.oidc.as_ref().map(|config| -> Result { + Ok(OidcClient { config: config.clone(), + client_secret: if let Some(client_secret_file) = &config.client_secret_file { + std::fs::read_to_string(client_secret_file) + .map(ClientSecret::new) + .map_err(|err| err!("Failed to read OIDC client secret file: {err}"))? + } else if let Some(client_secret) = &config.client_secret { + client_secret.clone() + } else { + // The config check function should cause an early exit before this happens + panic!("neither client secret or client secret file were set"); + }, machine: SetOnce::new(), // This isn't in the client service because it has to use the `reqwest` shipped by `openidconnect` client: reqwest::ClientBuilder::new() @@ -145,12 +157,13 @@ impl crate::Service for Service { .danger_accept_invalid_certs(args.server.config.allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure) .build() .expect("client should build") - }), + })} + ).transpose()?, })) } async fn worker(self: Arc) -> Result { - if let Some(OidcClient { config, machine, client }) = &self.client { + if let Some(OidcClient { config, client_secret, machine, client }) = &self.client { let redirect_url = self .services .config @@ -170,7 +183,7 @@ impl crate::Service for Service { OidcClientMachine::from_provider_metadata( provider_metadata, config.client_id.clone(), - Some(config.client_secret.clone()), + Some(client_secret.clone()), ) .set_redirect_uri(RedirectUrl::from_url(redirect_url)), )