Option to generate a MAS config from an existing Synapse config
This is a best-effort conversion, which will warn about unsupported options.
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -6161,14 +6161,17 @@ dependencies = [
|
||||
"futures-util",
|
||||
"insta",
|
||||
"mas-config",
|
||||
"mas-iana",
|
||||
"mas-storage",
|
||||
"mas-storage-pg",
|
||||
"oauth2-types",
|
||||
"opentelemetry",
|
||||
"opentelemetry-semantic-conventions",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror-ext",
|
||||
@@ -6176,6 +6179,7 @@ dependencies = [
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"ulid",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use camino::Utf8PathBuf;
|
||||
use clap::Parser;
|
||||
use figment::Figment;
|
||||
use mas_config::{ConfigurationSection, RootConfig, SyncConfig};
|
||||
use mas_storage::SystemClock;
|
||||
use mas_storage::{Clock as _, SystemClock};
|
||||
use mas_storage_pg::MIGRATOR;
|
||||
use rand::SeedableRng;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
@@ -46,6 +46,10 @@ enum Subcommand {
|
||||
/// If not specified, the config will be written to stdout
|
||||
#[clap(short, long)]
|
||||
output: Option<Utf8PathBuf>,
|
||||
|
||||
/// Existing Synapse configuration used to generate the MAS config
|
||||
#[arg(short, long, action = clap::ArgAction::Append)]
|
||||
synapse_config: Vec<Utf8PathBuf>,
|
||||
},
|
||||
|
||||
/// Sync the clients and providers from the config file to the database
|
||||
@@ -88,14 +92,24 @@ impl Options {
|
||||
info!("Configuration file looks good");
|
||||
}
|
||||
|
||||
SC::Generate { output } => {
|
||||
SC::Generate {
|
||||
output,
|
||||
synapse_config,
|
||||
} => {
|
||||
let _span = info_span!("cli.config.generate").entered();
|
||||
let clock = SystemClock::default();
|
||||
|
||||
// XXX: we should disallow SeedableRng::from_entropy
|
||||
let rng = rand_chacha::ChaChaRng::from_entropy();
|
||||
let config = RootConfig::generate(rng).await?;
|
||||
let config = serde_yaml::to_string(&config)?;
|
||||
let mut rng = rand_chacha::ChaChaRng::from_entropy();
|
||||
let mut config = RootConfig::generate(&mut rng).await?;
|
||||
|
||||
if !synapse_config.is_empty() {
|
||||
info!("Adjusting MAS config to match Synapse config from {synapse_config:?}");
|
||||
let synapse_config = syn2mas::synapse_config::Config::load(&synapse_config)?;
|
||||
config = synapse_config.adjust_mas_config(config, &mut rng, clock.now());
|
||||
}
|
||||
|
||||
let config = serde_yaml::to_string(&config)?;
|
||||
if let Some(output) = output {
|
||||
info!("Writing configuration to {output:?}");
|
||||
let mut file = tokio::fs::File::create(output).await?;
|
||||
|
||||
@@ -16,6 +16,7 @@ bitflags.workspace = true
|
||||
camino.workspace = true
|
||||
figment.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
thiserror.workspace = true
|
||||
thiserror-ext.workspace = true
|
||||
tokio.workspace = true
|
||||
@@ -26,6 +27,7 @@ compact_str.workspace = true
|
||||
tracing.workspace = true
|
||||
futures-util = "0.3.31"
|
||||
rustc-hash = "2.1.1"
|
||||
url.workspace = true
|
||||
|
||||
rand.workspace = true
|
||||
rand_chacha = "0.3.1"
|
||||
@@ -33,7 +35,9 @@ uuid = "1.16.0"
|
||||
ulid = { workspace = true, features = ["uuid"] }
|
||||
|
||||
mas-config.workspace = true
|
||||
mas-iana.workspace = true
|
||||
mas-storage.workspace = true
|
||||
oauth2-types.workspace = true
|
||||
|
||||
opentelemetry.workspace = true
|
||||
opentelemetry-semantic-conventions.workspace = true
|
||||
|
||||
@@ -157,7 +157,7 @@ pub fn synapse_config_check(synapse_config: &Config) -> (Vec<CheckWarning>, Vec<
|
||||
));
|
||||
}
|
||||
|
||||
if synapse_config.enable_3pid_changes {
|
||||
if synapse_config.enable_3pid_changes == Some(true) {
|
||||
errors.push(CheckError::ThreepidChangesEnabled);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,21 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
mod oidc;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use camino::Utf8PathBuf;
|
||||
use chrono::{DateTime, Utc};
|
||||
use figment::providers::{Format, Yaml};
|
||||
use mas_config::{PasswordAlgorithm, PasswordHashingScheme};
|
||||
use rand::Rng;
|
||||
use serde::Deserialize;
|
||||
use sqlx::postgres::PgConnectOptions;
|
||||
use tracing::warn;
|
||||
use url::Url;
|
||||
|
||||
pub use self::oidc::OidcProvider;
|
||||
|
||||
/// The root of a Synapse configuration.
|
||||
/// This struct only includes fields which the Synapse-to-MAS migration is
|
||||
@@ -23,6 +32,8 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub password_config: PasswordSection,
|
||||
|
||||
pub bcrypt_rounds: Option<u32>,
|
||||
|
||||
#[serde(default)]
|
||||
pub allow_guest_access: bool,
|
||||
|
||||
@@ -31,11 +42,16 @@ pub struct Config {
|
||||
|
||||
#[serde(default)]
|
||||
pub enable_registration_captcha: bool,
|
||||
pub recaptcha_public_key: Option<String>,
|
||||
pub recaptcha_private_key: Option<String>,
|
||||
|
||||
/// Normally this defaults to true, but when MAS integration is enabled in
|
||||
/// Synapse it defaults to false.
|
||||
#[serde(default)]
|
||||
pub enable_3pid_changes: bool,
|
||||
pub enable_3pid_changes: Option<bool>,
|
||||
|
||||
#[serde(default = "default_true")]
|
||||
enable_set_display_name: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub user_consent: Option<UserConsentSection>,
|
||||
@@ -67,6 +83,8 @@ pub struct Config {
|
||||
pub oidc_providers: Vec<OidcProvider>,
|
||||
|
||||
pub server_name: String,
|
||||
|
||||
pub public_baseurl: Option<Url>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -100,21 +118,97 @@ impl Config {
|
||||
let mut out = BTreeMap::new();
|
||||
|
||||
if let Some(provider) = &self.oidc_config {
|
||||
if provider.issuer.is_some() {
|
||||
if provider.has_required_fields() {
|
||||
let mut provider = provider.clone();
|
||||
// The legacy configuration has an implied IdP ID of `oidc`.
|
||||
out.insert("oidc".to_owned(), provider.clone());
|
||||
let idp_id = provider.idp_id.take().unwrap_or("oidc".to_owned());
|
||||
provider.idp_id = Some(idp_id.clone());
|
||||
out.insert(idp_id, provider);
|
||||
}
|
||||
}
|
||||
|
||||
for provider in &self.oidc_providers {
|
||||
if let Some(idp_id) = &provider.idp_id {
|
||||
let mut provider = provider.clone();
|
||||
let idp_id = match provider.idp_id.take() {
|
||||
None => "oidc".to_owned(),
|
||||
Some(idp_id) if idp_id == "oidc" => idp_id,
|
||||
// Synapse internally prefixes the IdP IDs with `oidc-`.
|
||||
out.insert(format!("oidc-{idp_id}"), provider.clone());
|
||||
}
|
||||
Some(idp_id) => format!("oidc-{idp_id}"),
|
||||
};
|
||||
provider.idp_id = Some(idp_id.clone());
|
||||
out.insert(idp_id, provider);
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// Adjust a MAS configuration to match this Synapse configuration.
|
||||
#[must_use]
|
||||
pub fn adjust_mas_config(
|
||||
self,
|
||||
mut mas_config: mas_config::RootConfig,
|
||||
rng: &mut impl Rng,
|
||||
now: DateTime<Utc>,
|
||||
) -> mas_config::RootConfig {
|
||||
let providers = self.all_oidc_providers();
|
||||
for provider in providers.into_values() {
|
||||
let Some(mas_provider_config) = provider.into_mas_config(rng, now) else {
|
||||
// TODO: better log message
|
||||
warn!("Could not convert OIDC provider to MAS config");
|
||||
continue;
|
||||
};
|
||||
|
||||
mas_config
|
||||
.upstream_oauth2
|
||||
.providers
|
||||
.push(mas_provider_config);
|
||||
}
|
||||
|
||||
// TODO: manage when the option is not set
|
||||
if let Some(enable_3pid_changes) = self.enable_3pid_changes {
|
||||
mas_config.account.email_change_allowed = enable_3pid_changes;
|
||||
}
|
||||
mas_config.account.displayname_change_allowed = self.enable_set_display_name;
|
||||
if self.password_config.enabled {
|
||||
mas_config.passwords.enabled = true;
|
||||
mas_config.passwords.schemes = vec![
|
||||
// This is the password hashing scheme synapse uses
|
||||
PasswordHashingScheme {
|
||||
version: 1,
|
||||
algorithm: PasswordAlgorithm::Bcrypt,
|
||||
cost: self.bcrypt_rounds,
|
||||
secret: self.password_config.pepper,
|
||||
secret_file: None,
|
||||
},
|
||||
// Use the default algorithm MAS uses as a second hashing scheme, so that users
|
||||
// will get their password hash upgraded to a more modern algorithm over time
|
||||
PasswordHashingScheme {
|
||||
version: 2,
|
||||
algorithm: PasswordAlgorithm::default(),
|
||||
cost: None,
|
||||
secret: None,
|
||||
secret_file: None,
|
||||
},
|
||||
];
|
||||
|
||||
mas_config.account.password_registration_enabled = self.enable_registration;
|
||||
} else {
|
||||
mas_config.passwords.enabled = false;
|
||||
}
|
||||
|
||||
if self.enable_registration_captcha {
|
||||
mas_config.captcha.service = Some(mas_config::CaptchaServiceKind::RecaptchaV2);
|
||||
mas_config.captcha.site_key = self.recaptcha_public_key;
|
||||
mas_config.captcha.secret_key = self.recaptcha_private_key;
|
||||
}
|
||||
|
||||
mas_config.matrix.homeserver = self.server_name;
|
||||
if let Some(public_baseurl) = self.public_baseurl {
|
||||
mas_config.matrix.endpoint = public_baseurl;
|
||||
}
|
||||
|
||||
mas_config
|
||||
}
|
||||
}
|
||||
|
||||
/// The `database` section of the Synapse configuration.
|
||||
@@ -215,17 +309,6 @@ pub struct EnableableSection {
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct OidcProvider {
|
||||
/// At least for `oidc_config`, if the dict is present but left empty then
|
||||
/// the config should be ignored, so this field must be optional.
|
||||
pub issuer: Option<String>,
|
||||
|
||||
/// Required, except for the old `oidc_config` where this is implied to be
|
||||
/// "oidc".
|
||||
pub idp_id: Option<String>,
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
347
crates/syn2mas/src/synapse_reader/config/oidc.rs
Normal file
347
crates/syn2mas/src/synapse_reader/config/oidc.rs
Normal file
@@ -0,0 +1,347 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
use std::{collections::BTreeMap, str::FromStr as _};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use mas_config::{
|
||||
UpstreamOAuth2ClaimsImports, UpstreamOAuth2DiscoveryMode, UpstreamOAuth2ImportAction,
|
||||
UpstreamOAuth2PkceMethod, UpstreamOAuth2ResponseMode, UpstreamOAuth2TokenAuthMethod,
|
||||
};
|
||||
use mas_iana::jose::JsonWebSignatureAlg;
|
||||
use oauth2_types::scope::{OPENID, Scope, ScopeToken};
|
||||
use rand::Rng;
|
||||
use serde::Deserialize;
|
||||
use tracing::warn;
|
||||
use ulid::Ulid;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
enum UserMappingProviderModule {
|
||||
#[default]
|
||||
#[serde(rename = "synapse.handlers.oidc.JinjaOidcMappingProvider")]
|
||||
Jinja,
|
||||
|
||||
#[serde(rename = "synapse.handlers.oidc_handler.JinjaOidcMappingProvider")]
|
||||
JinjaLegacy,
|
||||
|
||||
#[serde(other)]
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
struct UserMappingProviderConfig {
|
||||
subject_template: Option<String>,
|
||||
subject_claim: Option<String>,
|
||||
localpart_template: Option<String>,
|
||||
display_name_template: Option<String>,
|
||||
email_template: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
confirm_localpart: bool,
|
||||
}
|
||||
|
||||
impl UserMappingProviderConfig {
|
||||
fn into_mas_config(self) -> UpstreamOAuth2ClaimsImports {
|
||||
let mut config = UpstreamOAuth2ClaimsImports::default();
|
||||
|
||||
match (self.subject_claim, self.subject_template) {
|
||||
(Some(_), Some(subject_template)) => {
|
||||
warn!(
|
||||
"Both `subject_claim` and `subject_template` options are set, using `subject_template`."
|
||||
);
|
||||
config.subject.template = Some(subject_template);
|
||||
}
|
||||
(None, Some(subject_template)) => {
|
||||
config.subject.template = Some(subject_template);
|
||||
}
|
||||
(Some(subject_claim), None) => {
|
||||
config.subject.template = Some(format!("{{{{ user.{subject_claim} }}}}"));
|
||||
}
|
||||
(None, None) => {}
|
||||
}
|
||||
|
||||
if let Some(localpart_template) = self.localpart_template {
|
||||
config.localpart.template = Some(localpart_template);
|
||||
config.localpart.action = if self.confirm_localpart {
|
||||
UpstreamOAuth2ImportAction::Suggest
|
||||
} else {
|
||||
UpstreamOAuth2ImportAction::Require
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(displayname_template) = self.display_name_template {
|
||||
config.displayname.template = Some(displayname_template);
|
||||
config.displayname.action = if self.confirm_localpart {
|
||||
UpstreamOAuth2ImportAction::Suggest
|
||||
} else {
|
||||
UpstreamOAuth2ImportAction::Force
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(email_template) = self.email_template {
|
||||
config.email.template = Some(email_template);
|
||||
config.email.action = if self.confirm_localpart {
|
||||
UpstreamOAuth2ImportAction::Suggest
|
||||
} else {
|
||||
UpstreamOAuth2ImportAction::Force
|
||||
};
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
struct UserMappingProvider {
|
||||
#[serde(default)]
|
||||
module: UserMappingProviderModule,
|
||||
#[serde(default)]
|
||||
config: UserMappingProviderConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum PkceMethod {
|
||||
#[default]
|
||||
Auto,
|
||||
Always,
|
||||
Never,
|
||||
#[serde(other)]
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum UserProfileMethod {
|
||||
#[default]
|
||||
Auto,
|
||||
UserinfoEndpoint,
|
||||
#[serde(other)]
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[expect(clippy::struct_excessive_bools)]
|
||||
pub struct OidcProvider {
|
||||
pub issuer: Option<String>,
|
||||
|
||||
/// Required, except for the old `oidc_config` where this is implied to be
|
||||
/// "oidc".
|
||||
pub idp_id: Option<String>,
|
||||
|
||||
idp_name: Option<String>,
|
||||
idp_brand: Option<String>,
|
||||
|
||||
#[serde(default = "default_true")]
|
||||
discover: bool,
|
||||
|
||||
client_id: Option<String>,
|
||||
client_secret: Option<String>,
|
||||
|
||||
// Unsupported, we want to shout about it
|
||||
client_secret_path: Option<String>,
|
||||
|
||||
// Unsupported, we want to shout about it
|
||||
client_secret_jwt_key: Option<serde_json::Value>,
|
||||
client_auth_method: Option<UpstreamOAuth2TokenAuthMethod>,
|
||||
#[serde(default)]
|
||||
pkce_method: PkceMethod,
|
||||
// Unsupported, we want to shout about it
|
||||
id_token_signing_alg_values_supported: Option<Vec<String>>,
|
||||
scopes: Option<Vec<String>>,
|
||||
authorization_endpoint: Option<Url>,
|
||||
token_endpoint: Option<Url>,
|
||||
userinfo_endpoint: Option<Url>,
|
||||
jwks_uri: Option<Url>,
|
||||
#[serde(default)]
|
||||
skip_verification: bool,
|
||||
|
||||
// Unsupported, we want to shout about it
|
||||
#[serde(default)]
|
||||
backchannel_logout_enabled: bool,
|
||||
|
||||
#[serde(default)]
|
||||
user_profile_method: UserProfileMethod,
|
||||
|
||||
// Unsupported, we want to shout about it
|
||||
attribute_requirements: Option<serde_json::Value>,
|
||||
|
||||
// Unsupported, we want to shout about it
|
||||
#[serde(default = "default_true")]
|
||||
enable_registration: bool,
|
||||
#[serde(default)]
|
||||
additional_authorization_parameters: BTreeMap<String, String>,
|
||||
#[serde(default)]
|
||||
user_mapping_provider: UserMappingProvider,
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
impl OidcProvider {
|
||||
/// Returns true if the two 'required' fields are set. This is used to
|
||||
/// ignore an empty dict on the `oidc_config` section.
|
||||
#[must_use]
|
||||
pub(crate) fn has_required_fields(&self) -> bool {
|
||||
self.issuer.is_some() && self.client_id.is_some()
|
||||
}
|
||||
|
||||
/// Map this Synapse OIDC provider config to a MAS upstream provider config.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub(crate) fn into_mas_config(
|
||||
self,
|
||||
rng: &mut impl Rng,
|
||||
now: DateTime<Utc>,
|
||||
) -> Option<mas_config::UpstreamOAuth2Provider> {
|
||||
let client_id = self.client_id?;
|
||||
|
||||
if self.client_secret_path.is_some() {
|
||||
warn!(
|
||||
"The `client_secret_path` option is not supported, ignoring. You *will* need to include the secret in the `client_secret` field."
|
||||
);
|
||||
}
|
||||
|
||||
if self.client_secret_jwt_key.is_some() {
|
||||
warn!("The `client_secret_jwt_key` option is not supported, ignoring.");
|
||||
}
|
||||
|
||||
if self.attribute_requirements.is_some() {
|
||||
warn!("The `attribute_requirements` option is not supported, ignoring.");
|
||||
}
|
||||
|
||||
if self.id_token_signing_alg_values_supported.is_some() {
|
||||
warn!("The `id_token_signing_alg_values_supported` option is not supported, ignoring.");
|
||||
}
|
||||
|
||||
if self.backchannel_logout_enabled {
|
||||
warn!("The `backchannel_logout_enabled` option is not supported, ignoring.");
|
||||
}
|
||||
|
||||
if !self.enable_registration {
|
||||
warn!(
|
||||
"Setting the `enable_registration` option to `false` is not supported, ignoring."
|
||||
);
|
||||
}
|
||||
|
||||
let scope: Scope = match self.scopes {
|
||||
None => [OPENID].into_iter().collect(), // Synapse defaults to the 'openid' scope
|
||||
Some(scopes) => scopes
|
||||
.into_iter()
|
||||
.filter_map(|scope| match ScopeToken::from_str(&scope) {
|
||||
Ok(scope) => Some(scope),
|
||||
Err(err) => {
|
||||
warn!("OIDC provider scope '{scope}' is invalid: {err}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let id = Ulid::from_datetime_with_source(now.into(), rng);
|
||||
|
||||
let token_endpoint_auth_method = self.client_auth_method.unwrap_or_else(|| {
|
||||
// The token auth method defaults to 'none' if no client_secret is set and
|
||||
// 'client_secret_basic' otherwise
|
||||
if self.client_secret.is_some() {
|
||||
UpstreamOAuth2TokenAuthMethod::ClientSecretBasic
|
||||
} else {
|
||||
UpstreamOAuth2TokenAuthMethod::None
|
||||
}
|
||||
});
|
||||
|
||||
let discovery_mode = match (self.discover, self.skip_verification) {
|
||||
(true, false) => UpstreamOAuth2DiscoveryMode::Oidc,
|
||||
(true, true) => UpstreamOAuth2DiscoveryMode::Insecure,
|
||||
(false, _) => UpstreamOAuth2DiscoveryMode::Disabled,
|
||||
};
|
||||
|
||||
let pkce_method = match self.pkce_method {
|
||||
PkceMethod::Auto => UpstreamOAuth2PkceMethod::Auto,
|
||||
PkceMethod::Always => UpstreamOAuth2PkceMethod::Always,
|
||||
PkceMethod::Never => UpstreamOAuth2PkceMethod::Never,
|
||||
PkceMethod::Other => {
|
||||
warn!(
|
||||
"The `pkce_method` option is not supported, expected 'auto', 'always', or 'never'; assuming 'auto'."
|
||||
);
|
||||
UpstreamOAuth2PkceMethod::default()
|
||||
}
|
||||
};
|
||||
|
||||
// "auto" doesn't mean the same thing depending on whether we request the openid
|
||||
// scope or not
|
||||
let has_openid_scope = scope.contains(&OPENID);
|
||||
let fetch_userinfo = match self.user_profile_method {
|
||||
UserProfileMethod::Auto => has_openid_scope,
|
||||
UserProfileMethod::UserinfoEndpoint => true,
|
||||
UserProfileMethod::Other => {
|
||||
warn!(
|
||||
"The `user_profile_method` option is not supported, expected 'auto' or 'userinfo_endpoint'; assuming 'auto'."
|
||||
);
|
||||
has_openid_scope
|
||||
}
|
||||
};
|
||||
|
||||
// Check if there is a `response_mode` set in the additional authorization
|
||||
// parameters
|
||||
let mut additional_authorization_parameters = self.additional_authorization_parameters;
|
||||
let response_mode = if let Some(response_mode) =
|
||||
additional_authorization_parameters.remove("response_mode")
|
||||
{
|
||||
match response_mode.to_ascii_lowercase().as_str() {
|
||||
"query" => Some(UpstreamOAuth2ResponseMode::Query),
|
||||
"form_post" => Some(UpstreamOAuth2ResponseMode::FormPost),
|
||||
_ => {
|
||||
warn!(
|
||||
"Invalid `response_mode` in the `additional_authorization_parameters` option, expected 'query' or 'form_post'; ignoring."
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let claims_imports = if matches!(
|
||||
self.user_mapping_provider.module,
|
||||
UserMappingProviderModule::Other
|
||||
) {
|
||||
warn!(
|
||||
"The `user_mapping_provider` module specified is not supported, ignoring. Please adjust the `claims_imports` to match the mapping provider behaviour."
|
||||
);
|
||||
UpstreamOAuth2ClaimsImports::default()
|
||||
} else {
|
||||
self.user_mapping_provider.config.into_mas_config()
|
||||
};
|
||||
|
||||
Some(mas_config::UpstreamOAuth2Provider {
|
||||
enabled: true,
|
||||
id,
|
||||
synapse_idp_id: self.idp_id,
|
||||
issuer: self.issuer,
|
||||
human_name: self.idp_name,
|
||||
brand_name: self.idp_brand,
|
||||
client_id,
|
||||
client_secret: self.client_secret,
|
||||
token_endpoint_auth_method,
|
||||
sign_in_with_apple: None,
|
||||
token_endpoint_auth_signing_alg: None,
|
||||
id_token_signed_response_alg: JsonWebSignatureAlg::Rs256,
|
||||
scope: scope.to_string(),
|
||||
discovery_mode,
|
||||
pkce_method,
|
||||
fetch_userinfo,
|
||||
userinfo_signed_response_alg: None,
|
||||
authorization_endpoint: self.authorization_endpoint,
|
||||
userinfo_endpoint: self.userinfo_endpoint,
|
||||
token_endpoint: self.token_endpoint,
|
||||
jwks_uri: self.jwks_uri,
|
||||
response_mode,
|
||||
claims_imports,
|
||||
additional_authorization_parameters,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user