From 407f3cb4fa260daf772b1b569b680570dc685ae5 Mon Sep 17 00:00:00 2001 From: defaultdino Date: Fri, 3 Apr 2026 17:41:37 +0200 Subject: [PATCH 01/11] distinguish password vs upstream oauth registration token requirements --- crates/cli/src/sync.rs | 1 + crates/cli/src/util.rs | 2 +- crates/config/src/sections/account.rs | 12 +++-- crates/config/src/sections/upstream_oauth2.rs | 6 +++ crates/data-model/src/site_config.rs | 2 +- .../src/upstream_oauth2/provider.rs | 1 + crates/handlers/src/admin/model.rs | 8 ++++ crates/handlers/src/admin/v1/site_config.rs | 6 +-- .../src/admin/v1/upstream_oauth_links/mod.rs | 1 + .../admin/v1/upstream_oauth_providers/get.rs | 1 + .../admin/v1/upstream_oauth_providers/list.rs | 3 ++ crates/handlers/src/cleanup_tests.rs | 1 + crates/handlers/src/test_utils.rs | 2 +- crates/handlers/src/upstream_oauth2/cache.rs | 1 + crates/handlers/src/upstream_oauth2/link.rs | 7 +++ crates/handlers/src/views/login.rs | 2 + .../src/views/register/steps/finish.rs | 30 +++++++++++- ...ea304d43c336ce80723789ff3e66c0dd4d86c.json | 46 ------------------ ...5192f1b5c133fcda65894add35dc5ae62295.json} | 5 +- ...1462c443e686349cca3e9788dd93e21aa025e.json | 47 +++++++++++++++++++ ...b7b05caef229399ea0f61e0d7a5788034309.json} | 10 +++- ...a1b3cc6e0991897274636afefcd1a62bdbff.json} | 10 +++- ..._providers_registration_token_required.sql | 4 ++ crates/storage-pg/src/upstream_oauth2/mod.rs | 3 ++ .../src/upstream_oauth2/provider.rs | 22 +++++++-- crates/storage-pg/src/user/registration.rs | 1 + crates/storage-pg/src/user/tests.rs | 1 + .../storage/src/upstream_oauth2/provider.rs | 3 ++ .../syn2mas/src/synapse_reader/config/oidc.rs | 1 + crates/templates/src/context.rs | 1 + 30 files changed, 171 insertions(+), 69 deletions(-) delete mode 100644 crates/storage-pg/.sqlx/query-0f2ea548e00b080502edc04ee97ea304d43c336ce80723789ff3e66c0dd4d86c.json rename crates/storage-pg/.sqlx/{query-3312f901f70c3b69e0d315206c31ffe11da64835ae297c9277271b8971d5de81.json => query-212e0ce75c5e8b73e95b3a9505365192f1b5c133fcda65894add35dc5ae62295.json} (80%) create mode 100644 crates/storage-pg/.sqlx/query-82aaee10fac6db827cc60f8b2571462c443e686349cca3e9788dd93e21aa025e.json rename crates/storage-pg/.sqlx/{query-6589987e88fa9dbbd2bd48acd910e08bab57721007c64ef2597cb09a62100792.json => query-c80f6e8169a4b0e76168d851bcb1b7b05caef229399ea0f61e0d7a5788034309.json} (91%) rename crates/storage-pg/.sqlx/{query-99394fbd9c07d6d24429934b3f7344dfab024b42e47ddc7bd9e551897ba6e9b8.json => query-ec53297b6bdd23bc15bda823869fa1b3cc6e0991897274636afefcd1a62bdbff.json} (90%) create mode 100644 crates/storage-pg/migrations/20260403144138_upstream_oauth2_providers_registration_token_required.sql diff --git a/crates/cli/src/sync.rs b/crates/cli/src/sync.rs index c4aeb9a9c..d7a331d11 100644 --- a/crates/cli/src/sync.rs +++ b/crates/cli/src/sync.rs @@ -341,6 +341,7 @@ pub async fn config_sync( forward_login_hint: provider.forward_login_hint, ui_order, on_backchannel_logout, + registration_token_required: provider.registration_token_required, }, ) .await?; diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index 454276150..6d3d0ba52 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -223,7 +223,7 @@ pub fn site_config_from_config( password_registration_enabled: password_config.enabled() && account_config.password_registration_enabled, password_registration_email_required: account_config.password_registration_email_required, - registration_token_required: account_config.registration_token_required, + password_registration_token_required: account_config.password_registration_token_required, email_change_allowed: account_config.email_change_allowed, displayname_change_allowed: account_config.displayname_change_allowed, password_change_allowed: password_config.enabled() diff --git a/crates/config/src/sections/account.rs b/crates/config/src/sections/account.rs index 2b6538a2b..a861e713d 100644 --- a/crates/config/src/sections/account.rs +++ b/crates/config/src/sections/account.rs @@ -86,8 +86,12 @@ pub struct AccountConfig { /// When enabled, users must provide a valid registration token during /// password registration. This has no effect if password registration /// is disabled. - #[serde(default = "default_false", skip_serializing_if = "is_default_false")] - pub registration_token_required: bool, + #[serde( + default = "default_false", + skip_serializing_if = "is_default_false", + alias = "registration_token_required" + )] + pub password_registration_token_required: bool, } impl Default for AccountConfig { @@ -101,7 +105,7 @@ impl Default for AccountConfig { password_recovery_enabled: default_false(), account_deactivation_allowed: default_true(), login_with_email_allowed: default_false(), - registration_token_required: default_false(), + password_registration_token_required: default_false(), } } } @@ -116,7 +120,7 @@ impl AccountConfig { && is_default_false(&self.password_recovery_enabled) && is_default_true(&self.account_deactivation_allowed) && is_default_false(&self.login_with_email_allowed) - && is_default_false(&self.registration_token_required) + && is_default_false(&self.password_registration_token_required) } } diff --git a/crates/config/src/sections/upstream_oauth2.rs b/crates/config/src/sections/upstream_oauth2.rs index 40591b004..ee086cade 100644 --- a/crates/config/src/sections/upstream_oauth2.rs +++ b/crates/config/src/sections/upstream_oauth2.rs @@ -693,6 +693,12 @@ pub struct Provider { /// Defaults to `do_nothing`. #[serde(default, skip_serializing_if = "OnBackchannelLogout::is_default")] pub on_backchannel_logout: OnBackchannelLogout, + + /// Whether or not to require a registration token on OAuth2 auth + /// + /// Defaults to `false` + #[serde(default)] + pub registration_token_required: bool, } impl Provider { diff --git a/crates/data-model/src/site_config.rs b/crates/data-model/src/site_config.rs index bb92dc3e4..7adb683ed 100644 --- a/crates/data-model/src/site_config.rs +++ b/crates/data-model/src/site_config.rs @@ -77,7 +77,7 @@ pub struct SiteConfig { pub password_registration_email_required: bool, /// Whether registration tokens are required for password registrations. - pub registration_token_required: bool, + pub password_registration_token_required: bool, /// Whether users can change their email. pub email_change_allowed: bool, diff --git a/crates/data-model/src/upstream_oauth2/provider.rs b/crates/data-model/src/upstream_oauth2/provider.rs index 94f6c2e51..7a7bb9165 100644 --- a/crates/data-model/src/upstream_oauth2/provider.rs +++ b/crates/data-model/src/upstream_oauth2/provider.rs @@ -285,6 +285,7 @@ pub struct UpstreamOAuthProvider { pub additional_authorization_parameters: Vec<(String, String)>, pub forward_login_hint: bool, pub on_backchannel_logout: OnBackchannelLogout, + pub registration_token_required: bool, } impl PartialOrd for UpstreamOAuthProvider { diff --git a/crates/handlers/src/admin/model.rs b/crates/handlers/src/admin/model.rs index 7936c02f8..f071c089f 100644 --- a/crates/handlers/src/admin/model.rs +++ b/crates/handlers/src/admin/model.rs @@ -723,6 +723,10 @@ pub struct UpstreamOAuthProvider { /// When the provider was disabled. If null, the provider is enabled. disabled_at: Option>, + + /// Whether a registration token is required for registrations via this + /// provider. + registration_token_required: bool, } impl From for UpstreamOAuthProvider { @@ -734,6 +738,7 @@ impl From for UpstreamOAuthProvider { brand_name: provider.brand_name, created_at: provider.created_at, disabled_at: provider.disabled_at, + registration_token_required: provider.registration_token_required, } } } @@ -758,6 +763,7 @@ impl UpstreamOAuthProvider { brand_name: Some("google".to_owned()), created_at: DateTime::default(), disabled_at: None, + registration_token_required: false, }, Self { id: Ulid::from_bytes([0x02; 16]), @@ -766,6 +772,7 @@ impl UpstreamOAuthProvider { brand_name: Some("apple".to_owned()), created_at: DateTime::default(), disabled_at: Some(DateTime::default()), + registration_token_required: false, }, Self { id: Ulid::from_bytes([0x03; 16]), @@ -774,6 +781,7 @@ impl UpstreamOAuthProvider { brand_name: None, created_at: DateTime::default(), disabled_at: None, + registration_token_required: true, }, ] } diff --git a/crates/handlers/src/admin/v1/site_config.rs b/crates/handlers/src/admin/v1/site_config.rs index 40a5db51a..17e95e5c5 100644 --- a/crates/handlers/src/admin/v1/site_config.rs +++ b/crates/handlers/src/admin/v1/site_config.rs @@ -26,7 +26,7 @@ pub struct SiteConfig { pub password_registration_email_required: bool, /// Whether registration tokens are required for password registrations. - pub registration_token_required: bool, + pub password_registration_token_required: bool, /// Whether users can change their email. pub email_change_allowed: bool, @@ -63,7 +63,7 @@ pub fn doc(operation: TransformOperation) -> TransformOperation { password_login_enabled: true, password_registration_enabled: true, password_registration_email_required: true, - registration_token_required: true, + password_registration_token_required: true, email_change_allowed: true, displayname_change_allowed: true, password_change_allowed: true, @@ -85,7 +85,7 @@ pub async fn handler( password_login_enabled: site_config.password_login_enabled, password_registration_enabled: site_config.password_registration_enabled, password_registration_email_required: site_config.password_registration_email_required, - registration_token_required: site_config.registration_token_required, + password_registration_token_required: site_config.password_registration_token_required, email_change_allowed: site_config.email_change_allowed, displayname_change_allowed: site_config.displayname_change_allowed, password_change_allowed: site_config.password_change_allowed, diff --git a/crates/handlers/src/admin/v1/upstream_oauth_links/mod.rs b/crates/handlers/src/admin/v1/upstream_oauth_links/mod.rs index 3433aa3ca..33a16f1da 100644 --- a/crates/handlers/src/admin/v1/upstream_oauth_links/mod.rs +++ b/crates/handlers/src/admin/v1/upstream_oauth_links/mod.rs @@ -51,6 +51,7 @@ mod test_utils { forward_login_hint: false, ui_order: 0, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, } } } diff --git a/crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs b/crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs index 3700e1a65..410d3d279 100644 --- a/crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs +++ b/crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs @@ -119,6 +119,7 @@ mod tests { forward_login_hint: false, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 0, + registration_token_required: false, }; let provider = repo diff --git a/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs b/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs index d70bbd299..69f57e95c 100644 --- a/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs +++ b/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs @@ -191,6 +191,7 @@ mod tests { forward_login_hint: false, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 0, + registration_token_required: false, }; repo.upstream_oauth_provider() @@ -223,6 +224,7 @@ mod tests { forward_login_hint: false, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 1, + registration_token_required: false, }; let disabled_provider = repo @@ -262,6 +264,7 @@ mod tests { forward_login_hint: false, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 2, + registration_token_required: false, }; repo.upstream_oauth_provider() diff --git a/crates/handlers/src/cleanup_tests.rs b/crates/handlers/src/cleanup_tests.rs index 7b1a74f53..a0bcdba30 100644 --- a/crates/handlers/src/cleanup_tests.rs +++ b/crates/handlers/src/cleanup_tests.rs @@ -200,6 +200,7 @@ async fn create_session_hierarchy( forward_login_hint: false, ui_order: 0, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::LogoutAll, + registration_token_required: false, }; let provider = repo diff --git a/crates/handlers/src/test_utils.rs b/crates/handlers/src/test_utils.rs index 521a4848d..c72a7d56e 100644 --- a/crates/handlers/src/test_utils.rs +++ b/crates/handlers/src/test_utils.rs @@ -137,7 +137,7 @@ pub fn test_site_config() -> SiteConfig { imprint: None, password_login_enabled: true, password_registration_enabled: true, - registration_token_required: false, + password_registration_token_required: false, email_change_allowed: true, displayname_change_allowed: true, password_change_allowed: true, diff --git a/crates/handlers/src/upstream_oauth2/cache.rs b/crates/handlers/src/upstream_oauth2/cache.rs index 0857bc2c1..c1aecf382 100644 --- a/crates/handlers/src/upstream_oauth2/cache.rs +++ b/crates/handlers/src/upstream_oauth2/cache.rs @@ -434,6 +434,7 @@ mod tests { additional_authorization_parameters: Vec::new(), forward_login_hint: false, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }; // Without any override, it should just use discovery diff --git a/crates/handlers/src/upstream_oauth2/link.rs b/crates/handlers/src/upstream_oauth2/link.rs index e081b0f79..0f27b5cba 100644 --- a/crates/handlers/src/upstream_oauth2/link.rs +++ b/crates/handlers/src/upstream_oauth2/link.rs @@ -1345,6 +1345,7 @@ mod tests { ui_order: 0, on_backchannel_logout: mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await @@ -1542,6 +1543,7 @@ mod tests { ui_order: 0, on_backchannel_logout: mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await @@ -1703,6 +1705,7 @@ mod tests { on_backchannel_logout: mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 0, + registration_token_required: false, }, ) .await @@ -1819,6 +1822,7 @@ mod tests { on_backchannel_logout: mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 0, + registration_token_required: false, }, ) .await @@ -1988,6 +1992,7 @@ mod tests { on_backchannel_logout: mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 0, + registration_token_required: false, }, ) .await @@ -2136,6 +2141,7 @@ mod tests { on_backchannel_logout: mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 0, + registration_token_required: false, }, ) .await @@ -2255,6 +2261,7 @@ mod tests { on_backchannel_logout: mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, ui_order: 0, + registration_token_required: false, }, ) .await diff --git a/crates/handlers/src/views/login.rs b/crates/handlers/src/views/login.rs index a4fef8eba..683ebe075 100644 --- a/crates/handlers/src/views/login.rs +++ b/crates/handlers/src/views/login.rs @@ -525,6 +525,7 @@ mod test { forward_login_hint: false, ui_order: 0, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await @@ -568,6 +569,7 @@ mod test { forward_login_hint: false, ui_order: 1, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await diff --git a/crates/handlers/src/views/register/steps/finish.rs b/crates/handlers/src/views/register/steps/finish.rs index af0b8ef9f..2b1cdda7c 100644 --- a/crates/handlers/src/views/register/steps/finish.rs +++ b/crates/handlers/src/views/register/steps/finish.rs @@ -120,8 +120,34 @@ pub(crate) async fn get( ))); } - // Check if the registration token is required and was provided - let registration_token = if site_config.registration_token_required { + let token_required = if registration.upstream_oauth_authorization_session_id.is_some() { + // load provider to let OAuth registrations check its + // per provider `registration_token_required` flag + let session_id = registration + .upstream_oauth_authorization_session_id + .context("Missing upstream OAuth session ID") + .map_err(InternalError::from_anyhow)?; + + let session = repo + .upstream_oauth_session() + .lookup(session_id) + .await? + .context("Could not load the upstream OAuth authorization session") + .map_err(InternalError::from_anyhow)?; + + let provider = repo + .upstream_oauth_provider() + .lookup(session.provider_id) + .await? + .context("Could not load the upstream OAuth provider") + .map_err(InternalError::from_anyhow)?; + + provider.registration_token_required + } else { + site_config.password_registration_token_required + }; + + let registration_token = if token_required { if let Some(registration_token_id) = registration.user_registration_token_id { let registration_token = repo .user_registration_token() diff --git a/crates/storage-pg/.sqlx/query-0f2ea548e00b080502edc04ee97ea304d43c336ce80723789ff3e66c0dd4d86c.json b/crates/storage-pg/.sqlx/query-0f2ea548e00b080502edc04ee97ea304d43c336ce80723789ff3e66c0dd4d86c.json deleted file mode 100644 index 1eb87fd3f..000000000 --- a/crates/storage-pg/.sqlx/query-0f2ea548e00b080502edc04ee97ea304d43c336ce80723789ff3e66c0dd4d86c.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO upstream_oauth_providers (\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n token_endpoint_auth_method,\n token_endpoint_signing_alg,\n id_token_signed_response_alg,\n fetch_userinfo,\n userinfo_signed_response_alg,\n client_id,\n encrypted_client_secret,\n claims_imports,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n jwks_uri_override,\n discovery_mode,\n pkce_mode,\n response_mode,\n additional_parameters,\n forward_login_hint,\n ui_order,\n on_backchannel_logout,\n created_at\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,\n $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,\n $21, $22, $23, $24, $25)\n ON CONFLICT (upstream_oauth_provider_id)\n DO UPDATE\n SET\n issuer = EXCLUDED.issuer,\n human_name = EXCLUDED.human_name,\n brand_name = EXCLUDED.brand_name,\n scope = EXCLUDED.scope,\n token_endpoint_auth_method = EXCLUDED.token_endpoint_auth_method,\n token_endpoint_signing_alg = EXCLUDED.token_endpoint_signing_alg,\n id_token_signed_response_alg = EXCLUDED.id_token_signed_response_alg,\n fetch_userinfo = EXCLUDED.fetch_userinfo,\n userinfo_signed_response_alg = EXCLUDED.userinfo_signed_response_alg,\n disabled_at = NULL,\n client_id = EXCLUDED.client_id,\n encrypted_client_secret = EXCLUDED.encrypted_client_secret,\n claims_imports = EXCLUDED.claims_imports,\n authorization_endpoint_override = EXCLUDED.authorization_endpoint_override,\n token_endpoint_override = EXCLUDED.token_endpoint_override,\n userinfo_endpoint_override = EXCLUDED.userinfo_endpoint_override,\n jwks_uri_override = EXCLUDED.jwks_uri_override,\n discovery_mode = EXCLUDED.discovery_mode,\n pkce_mode = EXCLUDED.pkce_mode,\n response_mode = EXCLUDED.response_mode,\n additional_parameters = EXCLUDED.additional_parameters,\n forward_login_hint = EXCLUDED.forward_login_hint,\n ui_order = EXCLUDED.ui_order,\n on_backchannel_logout = EXCLUDED.on_backchannel_logout\n RETURNING created_at\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Bool", - "Text", - "Text", - "Text", - "Jsonb", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Jsonb", - "Bool", - "Int4", - "Text", - "Timestamptz" - ] - }, - "nullable": [ - false - ] - }, - "hash": "0f2ea548e00b080502edc04ee97ea304d43c336ce80723789ff3e66c0dd4d86c" -} diff --git a/crates/storage-pg/.sqlx/query-3312f901f70c3b69e0d315206c31ffe11da64835ae297c9277271b8971d5de81.json b/crates/storage-pg/.sqlx/query-212e0ce75c5e8b73e95b3a9505365192f1b5c133fcda65894add35dc5ae62295.json similarity index 80% rename from crates/storage-pg/.sqlx/query-3312f901f70c3b69e0d315206c31ffe11da64835ae297c9277271b8971d5de81.json rename to crates/storage-pg/.sqlx/query-212e0ce75c5e8b73e95b3a9505365192f1b5c133fcda65894add35dc5ae62295.json index 3f837630f..de285dbba 100644 --- a/crates/storage-pg/.sqlx/query-3312f901f70c3b69e0d315206c31ffe11da64835ae297c9277271b8971d5de81.json +++ b/crates/storage-pg/.sqlx/query-212e0ce75c5e8b73e95b3a9505365192f1b5c133fcda65894add35dc5ae62295.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO upstream_oauth_providers (\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n token_endpoint_auth_method,\n token_endpoint_signing_alg,\n id_token_signed_response_alg,\n fetch_userinfo,\n userinfo_signed_response_alg,\n client_id,\n encrypted_client_secret,\n claims_imports,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n jwks_uri_override,\n discovery_mode,\n pkce_mode,\n response_mode,\n forward_login_hint,\n on_backchannel_logout,\n created_at\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11,\n $12, $13, $14, $15, $16, $17, $18, $19, $20,\n $21, $22, $23)\n ", + "query": "\n INSERT INTO upstream_oauth_providers (\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n token_endpoint_auth_method,\n token_endpoint_signing_alg,\n id_token_signed_response_alg,\n fetch_userinfo,\n userinfo_signed_response_alg,\n client_id,\n encrypted_client_secret,\n claims_imports,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n jwks_uri_override,\n discovery_mode,\n pkce_mode,\n response_mode,\n forward_login_hint,\n on_backchannel_logout,\n registration_token_required,\n created_at\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11,\n $12, $13, $14, $15, $16, $17, $18, $19, $20,\n $21, $22, $23, $24)\n ", "describe": { "columns": [], "parameters": { @@ -27,10 +27,11 @@ "Text", "Bool", "Text", + "Bool", "Timestamptz" ] }, "nullable": [] }, - "hash": "3312f901f70c3b69e0d315206c31ffe11da64835ae297c9277271b8971d5de81" + "hash": "212e0ce75c5e8b73e95b3a9505365192f1b5c133fcda65894add35dc5ae62295" } diff --git a/crates/storage-pg/.sqlx/query-82aaee10fac6db827cc60f8b2571462c443e686349cca3e9788dd93e21aa025e.json b/crates/storage-pg/.sqlx/query-82aaee10fac6db827cc60f8b2571462c443e686349cca3e9788dd93e21aa025e.json new file mode 100644 index 000000000..fd078bbc8 --- /dev/null +++ b/crates/storage-pg/.sqlx/query-82aaee10fac6db827cc60f8b2571462c443e686349cca3e9788dd93e21aa025e.json @@ -0,0 +1,47 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO upstream_oauth_providers (\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n token_endpoint_auth_method,\n token_endpoint_signing_alg,\n id_token_signed_response_alg,\n fetch_userinfo,\n userinfo_signed_response_alg,\n client_id,\n encrypted_client_secret,\n claims_imports,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n jwks_uri_override,\n discovery_mode,\n pkce_mode,\n response_mode,\n additional_parameters,\n forward_login_hint,\n ui_order,\n on_backchannel_logout,\n registration_token_required,\n created_at\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,\n $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,\n $21, $22, $23, $24, $25, $26)\n ON CONFLICT (upstream_oauth_provider_id)\n DO UPDATE\n SET\n issuer = EXCLUDED.issuer,\n human_name = EXCLUDED.human_name,\n brand_name = EXCLUDED.brand_name,\n scope = EXCLUDED.scope,\n token_endpoint_auth_method = EXCLUDED.token_endpoint_auth_method,\n token_endpoint_signing_alg = EXCLUDED.token_endpoint_signing_alg,\n id_token_signed_response_alg = EXCLUDED.id_token_signed_response_alg,\n fetch_userinfo = EXCLUDED.fetch_userinfo,\n userinfo_signed_response_alg = EXCLUDED.userinfo_signed_response_alg,\n disabled_at = NULL,\n client_id = EXCLUDED.client_id,\n encrypted_client_secret = EXCLUDED.encrypted_client_secret,\n claims_imports = EXCLUDED.claims_imports,\n authorization_endpoint_override = EXCLUDED.authorization_endpoint_override,\n token_endpoint_override = EXCLUDED.token_endpoint_override,\n userinfo_endpoint_override = EXCLUDED.userinfo_endpoint_override,\n jwks_uri_override = EXCLUDED.jwks_uri_override,\n discovery_mode = EXCLUDED.discovery_mode,\n pkce_mode = EXCLUDED.pkce_mode,\n response_mode = EXCLUDED.response_mode,\n additional_parameters = EXCLUDED.additional_parameters,\n forward_login_hint = EXCLUDED.forward_login_hint,\n ui_order = EXCLUDED.ui_order,\n on_backchannel_logout = EXCLUDED.on_backchannel_logout,\n registration_token_required = EXCLUDED.registration_token_required\n RETURNING created_at\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Bool", + "Text", + "Text", + "Text", + "Jsonb", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Jsonb", + "Bool", + "Int4", + "Text", + "Bool", + "Timestamptz" + ] + }, + "nullable": [ + false + ] + }, + "hash": "82aaee10fac6db827cc60f8b2571462c443e686349cca3e9788dd93e21aa025e" +} diff --git a/crates/storage-pg/.sqlx/query-6589987e88fa9dbbd2bd48acd910e08bab57721007c64ef2597cb09a62100792.json b/crates/storage-pg/.sqlx/query-c80f6e8169a4b0e76168d851bcb1b7b05caef229399ea0f61e0d7a5788034309.json similarity index 91% rename from crates/storage-pg/.sqlx/query-6589987e88fa9dbbd2bd48acd910e08bab57721007c64ef2597cb09a62100792.json rename to crates/storage-pg/.sqlx/query-c80f6e8169a4b0e76168d851bcb1b7b05caef229399ea0f61e0d7a5788034309.json index 6bd2768cc..7ecf3d931 100644 --- a/crates/storage-pg/.sqlx/query-6589987e88fa9dbbd2bd48acd910e08bab57721007c64ef2597cb09a62100792.json +++ b/crates/storage-pg/.sqlx/query-c80f6e8169a4b0e76168d851bcb1b7b05caef229399ea0f61e0d7a5788034309.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n id_token_signed_response_alg,\n fetch_userinfo,\n userinfo_signed_response_alg,\n created_at,\n disabled_at,\n claims_imports as \"claims_imports: Json\",\n jwks_uri_override,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n discovery_mode,\n pkce_mode,\n response_mode,\n additional_parameters as \"additional_parameters: Json>\",\n forward_login_hint,\n on_backchannel_logout\n FROM upstream_oauth_providers\n WHERE upstream_oauth_provider_id = $1\n ", + "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n id_token_signed_response_alg,\n fetch_userinfo,\n userinfo_signed_response_alg,\n created_at,\n disabled_at,\n claims_imports as \"claims_imports: Json\",\n jwks_uri_override,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n discovery_mode,\n pkce_mode,\n response_mode,\n additional_parameters as \"additional_parameters: Json>\",\n forward_login_hint,\n on_backchannel_logout,\n registration_token_required\n FROM upstream_oauth_providers\n WHERE upstream_oauth_provider_id = $1\n ", "describe": { "columns": [ { @@ -127,6 +127,11 @@ "ordinal": 24, "name": "on_backchannel_logout", "type_info": "Text" + }, + { + "ordinal": 25, + "name": "registration_token_required", + "type_info": "Bool" } ], "parameters": { @@ -159,8 +164,9 @@ true, true, false, + false, false ] }, - "hash": "6589987e88fa9dbbd2bd48acd910e08bab57721007c64ef2597cb09a62100792" + "hash": "c80f6e8169a4b0e76168d851bcb1b7b05caef229399ea0f61e0d7a5788034309" } diff --git a/crates/storage-pg/.sqlx/query-99394fbd9c07d6d24429934b3f7344dfab024b42e47ddc7bd9e551897ba6e9b8.json b/crates/storage-pg/.sqlx/query-ec53297b6bdd23bc15bda823869fa1b3cc6e0991897274636afefcd1a62bdbff.json similarity index 90% rename from crates/storage-pg/.sqlx/query-99394fbd9c07d6d24429934b3f7344dfab024b42e47ddc7bd9e551897ba6e9b8.json rename to crates/storage-pg/.sqlx/query-ec53297b6bdd23bc15bda823869fa1b3cc6e0991897274636afefcd1a62bdbff.json index eb1a801c4..dd993904f 100644 --- a/crates/storage-pg/.sqlx/query-99394fbd9c07d6d24429934b3f7344dfab024b42e47ddc7bd9e551897ba6e9b8.json +++ b/crates/storage-pg/.sqlx/query-ec53297b6bdd23bc15bda823869fa1b3cc6e0991897274636afefcd1a62bdbff.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n id_token_signed_response_alg,\n fetch_userinfo,\n userinfo_signed_response_alg,\n created_at,\n disabled_at,\n claims_imports as \"claims_imports: Json\",\n jwks_uri_override,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n discovery_mode,\n pkce_mode,\n response_mode,\n additional_parameters as \"additional_parameters: Json>\",\n forward_login_hint,\n on_backchannel_logout\n FROM upstream_oauth_providers\n WHERE disabled_at IS NULL\n ORDER BY ui_order ASC, upstream_oauth_provider_id ASC\n ", + "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n id_token_signed_response_alg,\n fetch_userinfo,\n userinfo_signed_response_alg,\n created_at,\n disabled_at,\n claims_imports as \"claims_imports: Json\",\n jwks_uri_override,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n discovery_mode,\n pkce_mode,\n response_mode,\n additional_parameters as \"additional_parameters: Json>\",\n forward_login_hint,\n on_backchannel_logout,\n registration_token_required\n\n FROM upstream_oauth_providers\n WHERE disabled_at IS NULL\n ORDER BY ui_order ASC, upstream_oauth_provider_id ASC\n ", "describe": { "columns": [ { @@ -127,6 +127,11 @@ "ordinal": 24, "name": "on_backchannel_logout", "type_info": "Text" + }, + { + "ordinal": 25, + "name": "registration_token_required", + "type_info": "Bool" } ], "parameters": { @@ -157,8 +162,9 @@ true, true, false, + false, false ] }, - "hash": "99394fbd9c07d6d24429934b3f7344dfab024b42e47ddc7bd9e551897ba6e9b8" + "hash": "ec53297b6bdd23bc15bda823869fa1b3cc6e0991897274636afefcd1a62bdbff" } diff --git a/crates/storage-pg/migrations/20260403144138_upstream_oauth2_providers_registration_token_required.sql b/crates/storage-pg/migrations/20260403144138_upstream_oauth2_providers_registration_token_required.sql new file mode 100644 index 000000000..d1c35003b --- /dev/null +++ b/crates/storage-pg/migrations/20260403144138_upstream_oauth2_providers_registration_token_required.sql @@ -0,0 +1,4 @@ +-- Adds a `registration_token_required` column to the UpstreamOauthProvider table + +ALTER TABLE upstream_oauth_providers + ADD COLUMN registration_token_required BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file diff --git a/crates/storage-pg/src/upstream_oauth2/mod.rs b/crates/storage-pg/src/upstream_oauth2/mod.rs index 12df9d5f0..7c912daf0 100644 --- a/crates/storage-pg/src/upstream_oauth2/mod.rs +++ b/crates/storage-pg/src/upstream_oauth2/mod.rs @@ -79,6 +79,7 @@ mod tests { forward_login_hint: false, ui_order: 0, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await @@ -359,6 +360,7 @@ mod tests { forward_login_hint: false, ui_order: 0, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await @@ -497,6 +499,7 @@ mod tests { forward_login_hint: false, ui_order: 0, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await diff --git a/crates/storage-pg/src/upstream_oauth2/provider.rs b/crates/storage-pg/src/upstream_oauth2/provider.rs index caade738d..b4d13c11c 100644 --- a/crates/storage-pg/src/upstream_oauth2/provider.rs +++ b/crates/storage-pg/src/upstream_oauth2/provider.rs @@ -73,6 +73,7 @@ struct ProviderLookup { additional_parameters: Option>>, forward_login_hint: bool, on_backchannel_logout: String, + registration_token_required: bool, } impl Node for ProviderLookup { @@ -234,6 +235,7 @@ impl TryFrom for UpstreamOAuthProvider { additional_authorization_parameters, forward_login_hint: value.forward_login_hint, on_backchannel_logout, + registration_token_required: value.registration_token_required, }) } } @@ -293,7 +295,8 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { response_mode, additional_parameters as "additional_parameters: Json>", forward_login_hint, - on_backchannel_logout + on_backchannel_logout, + registration_token_required FROM upstream_oauth_providers WHERE upstream_oauth_provider_id = $1 "#, @@ -357,10 +360,11 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { response_mode, forward_login_hint, on_backchannel_logout, + registration_token_required, created_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, - $21, $22, $23) + $21, $22, $23, $24) "#, Uuid::from(id), params.issuer.as_deref(), @@ -399,6 +403,7 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { params.response_mode.as_ref().map(ToString::to_string), params.forward_login_hint, params.on_backchannel_logout.as_str(), + params.registration_token_required, created_at, ) .traced() @@ -431,6 +436,7 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { additional_authorization_parameters: params.additional_authorization_parameters, on_backchannel_logout: params.on_backchannel_logout, forward_login_hint: params.forward_login_hint, + registration_token_required: params.registration_token_required }) } @@ -546,10 +552,11 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { forward_login_hint, ui_order, on_backchannel_logout, + registration_token_required, created_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, - $21, $22, $23, $24, $25) + $21, $22, $23, $24, $25, $26) ON CONFLICT (upstream_oauth_provider_id) DO UPDATE SET @@ -576,7 +583,8 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { additional_parameters = EXCLUDED.additional_parameters, forward_login_hint = EXCLUDED.forward_login_hint, ui_order = EXCLUDED.ui_order, - on_backchannel_logout = EXCLUDED.on_backchannel_logout + on_backchannel_logout = EXCLUDED.on_backchannel_logout, + registration_token_required = EXCLUDED.registration_token_required RETURNING created_at "#, Uuid::from(id), @@ -618,6 +626,7 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { params.forward_login_hint, params.ui_order, params.on_backchannel_logout.as_str(), + params.registration_token_required, created_at, ) .traced() @@ -650,6 +659,7 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { additional_authorization_parameters: params.additional_authorization_parameters, forward_login_hint: params.forward_login_hint, on_backchannel_logout: params.on_backchannel_logout, + registration_token_required: params.registration_token_required, }) } @@ -968,7 +978,9 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { response_mode, additional_parameters as "additional_parameters: Json>", forward_login_hint, - on_backchannel_logout + on_backchannel_logout, + registration_token_required + FROM upstream_oauth_providers WHERE disabled_at IS NULL ORDER BY ui_order ASC, upstream_oauth_provider_id ASC diff --git a/crates/storage-pg/src/user/registration.rs b/crates/storage-pg/src/user/registration.rs index fff1dd0ad..5a14907b7 100644 --- a/crates/storage-pg/src/user/registration.rs +++ b/crates/storage-pg/src/user/registration.rs @@ -993,6 +993,7 @@ mod tests { forward_login_hint: false, ui_order: 0, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await diff --git a/crates/storage-pg/src/user/tests.rs b/crates/storage-pg/src/user/tests.rs index be106c56f..4774e0b45 100644 --- a/crates/storage-pg/src/user/tests.rs +++ b/crates/storage-pg/src/user/tests.rs @@ -772,6 +772,7 @@ async fn test_user_session(pool: PgPool) { ui_order: 0, on_backchannel_logout: mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, ) .await diff --git a/crates/storage/src/upstream_oauth2/provider.rs b/crates/storage/src/upstream_oauth2/provider.rs index 256a74968..b51711059 100644 --- a/crates/storage/src/upstream_oauth2/provider.rs +++ b/crates/storage/src/upstream_oauth2/provider.rs @@ -105,6 +105,9 @@ pub struct UpstreamOAuthProviderParams { /// The behavior when receiving a backchannel logout notification pub on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout, + + /// Whether or not to require a registration token on OAuth2 auth + pub registration_token_required: bool, } /// Filter parameters for listing upstream OAuth 2.0 providers diff --git a/crates/syn2mas/src/synapse_reader/config/oidc.rs b/crates/syn2mas/src/synapse_reader/config/oidc.rs index 09baba165..440365dd9 100644 --- a/crates/syn2mas/src/synapse_reader/config/oidc.rs +++ b/crates/syn2mas/src/synapse_reader/config/oidc.rs @@ -347,6 +347,7 @@ impl OidcProvider { additional_authorization_parameters, forward_login_hint: self.forward_login_hint, on_backchannel_logout, + registration_token_required: false, }) } } diff --git a/crates/templates/src/context.rs b/crates/templates/src/context.rs index 25123970b..0600aa868 100644 --- a/crates/templates/src/context.rs +++ b/crates/templates/src/context.rs @@ -1739,6 +1739,7 @@ impl TemplateContext for UpstreamRegister { created_at: now, disabled_at: None, on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + registration_token_required: false, }, )]) } From 61baee703cdfc42775a6a5229f1f258dd03ff3db Mon Sep 17 00:00:00 2001 From: defaultdino Date: Fri, 3 Apr 2026 17:46:59 +0200 Subject: [PATCH 02/11] format --- crates/config/src/sections/upstream_oauth2.rs | 2 +- crates/handlers/src/views/register/steps/finish.rs | 5 ++++- crates/storage-pg/src/upstream_oauth2/provider.rs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/config/src/sections/upstream_oauth2.rs b/crates/config/src/sections/upstream_oauth2.rs index ee086cade..1c89307dd 100644 --- a/crates/config/src/sections/upstream_oauth2.rs +++ b/crates/config/src/sections/upstream_oauth2.rs @@ -695,7 +695,7 @@ pub struct Provider { pub on_backchannel_logout: OnBackchannelLogout, /// Whether or not to require a registration token on OAuth2 auth - /// + /// /// Defaults to `false` #[serde(default)] pub registration_token_required: bool, diff --git a/crates/handlers/src/views/register/steps/finish.rs b/crates/handlers/src/views/register/steps/finish.rs index 2b1cdda7c..697891a49 100644 --- a/crates/handlers/src/views/register/steps/finish.rs +++ b/crates/handlers/src/views/register/steps/finish.rs @@ -120,7 +120,10 @@ pub(crate) async fn get( ))); } - let token_required = if registration.upstream_oauth_authorization_session_id.is_some() { + let token_required = if registration + .upstream_oauth_authorization_session_id + .is_some() + { // load provider to let OAuth registrations check its // per provider `registration_token_required` flag let session_id = registration diff --git a/crates/storage-pg/src/upstream_oauth2/provider.rs b/crates/storage-pg/src/upstream_oauth2/provider.rs index b4d13c11c..de55d0ebd 100644 --- a/crates/storage-pg/src/upstream_oauth2/provider.rs +++ b/crates/storage-pg/src/upstream_oauth2/provider.rs @@ -436,7 +436,7 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { additional_authorization_parameters: params.additional_authorization_parameters, on_backchannel_logout: params.on_backchannel_logout, forward_login_hint: params.forward_login_hint, - registration_token_required: params.registration_token_required + registration_token_required: params.registration_token_required, }) } From 734221817516bf73967153b23c33e79dceb69913 Mon Sep 17 00:00:00 2001 From: defaultdino Date: Fri, 3 Apr 2026 19:22:57 +0200 Subject: [PATCH 03/11] fix provider.rs, format, and run update.sh --- crates/config/src/sections/upstream_oauth2.rs | 2 +- crates/storage-pg/src/iden.rs | 1 + .../src/upstream_oauth2/provider.rs | 7 ++++++ .../storage/src/upstream_oauth2/provider.rs | 2 +- docs/api/spec.json | 25 +++++++++++++------ docs/config.schema.json | 7 +++++- 6 files changed, 33 insertions(+), 11 deletions(-) diff --git a/crates/config/src/sections/upstream_oauth2.rs b/crates/config/src/sections/upstream_oauth2.rs index 1c89307dd..29e5d8704 100644 --- a/crates/config/src/sections/upstream_oauth2.rs +++ b/crates/config/src/sections/upstream_oauth2.rs @@ -694,7 +694,7 @@ pub struct Provider { #[serde(default, skip_serializing_if = "OnBackchannelLogout::is_default")] pub on_backchannel_logout: OnBackchannelLogout, - /// Whether or not to require a registration token on OAuth2 auth + /// Whether or not to require a registration token on `OAuth2` auth /// /// Defaults to `false` #[serde(default)] diff --git a/crates/storage-pg/src/iden.rs b/crates/storage-pg/src/iden.rs index f1af1a5d2..10030f929 100644 --- a/crates/storage-pg/src/iden.rs +++ b/crates/storage-pg/src/iden.rs @@ -156,6 +156,7 @@ pub enum UpstreamOAuthProviders { AuthorizationEndpointOverride, UserinfoEndpointOverride, OnBackchannelLogout, + RegistrationTokenRequired, } #[derive(sea_query::Iden)] diff --git a/crates/storage-pg/src/upstream_oauth2/provider.rs b/crates/storage-pg/src/upstream_oauth2/provider.rs index de55d0ebd..2229556e6 100644 --- a/crates/storage-pg/src/upstream_oauth2/provider.rs +++ b/crates/storage-pg/src/upstream_oauth2/provider.rs @@ -884,6 +884,13 @@ impl UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'_> { )), ProviderLookupIden::OnBackchannelLogout, ) + .expr_as( + Expr::col(( + UpstreamOAuthProviders::Table, + UpstreamOAuthProviders::RegistrationTokenRequired, + )), + ProviderLookupIden::RegistrationTokenRequired, + ) .from(UpstreamOAuthProviders::Table) .apply_filter(filter) .generate_pagination( diff --git a/crates/storage/src/upstream_oauth2/provider.rs b/crates/storage/src/upstream_oauth2/provider.rs index b51711059..df217b433 100644 --- a/crates/storage/src/upstream_oauth2/provider.rs +++ b/crates/storage/src/upstream_oauth2/provider.rs @@ -106,7 +106,7 @@ pub struct UpstreamOAuthProviderParams { /// The behavior when receiving a backchannel logout notification pub on_backchannel_logout: UpstreamOAuthProviderOnBackchannelLogout, - /// Whether or not to require a registration token on OAuth2 auth + /// Whether or not to require a registration token on `OAuth2` auth pub registration_token_required: bool, } diff --git a/docs/api/spec.json b/docs/api/spec.json index ac56910b8..ace81d912 100644 --- a/docs/api/spec.json +++ b/docs/api/spec.json @@ -36,7 +36,7 @@ "password_login_enabled": true, "password_registration_enabled": true, "password_registration_email_required": true, - "registration_token_required": true, + "password_registration_token_required": true, "email_change_allowed": true, "displayname_change_allowed": true, "password_change_allowed": true, @@ -4685,7 +4685,8 @@ "human_name": "Google", "brand_name": "google", "created_at": "1970-01-01T00:00:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01040G2081040G2081040G2081" @@ -4704,7 +4705,8 @@ "human_name": "Apple ID", "brand_name": "apple", "created_at": "1970-01-01T00:00:00Z", - "disabled_at": "1970-01-01T00:00:00Z" + "disabled_at": "1970-01-01T00:00:00Z", + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/02081040G2081040G2081040G2" @@ -4723,7 +4725,8 @@ "human_name": "Custom OAuth Provider", "brand_name": null, "created_at": "1970-01-01T00:00:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": true }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/030C1G60R30C1G60R30C1G60R3" @@ -4784,7 +4787,8 @@ "human_name": "Google", "brand_name": "google", "created_at": "1970-01-01T00:00:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01040G2081040G2081040G2081" @@ -4859,7 +4863,7 @@ "description": "Whether a valid email address is required for password registrations.", "type": "boolean" }, - "registration_token_required": { + "password_registration_token_required": { "description": "Whether registration tokens are required for password registrations.", "type": "boolean" }, @@ -4900,7 +4904,7 @@ "password_login_enabled", "password_registration_enabled", "password_registration_email_required", - "registration_token_required", + "password_registration_token_required", "email_change_allowed", "displayname_change_allowed", "password_change_allowed", @@ -7368,10 +7372,15 @@ "null" ], "format": "date-time" + }, + "registration_token_required": { + "description": "Whether a registration token is required for registrations via this\n provider.", + "type": "boolean" } }, "required": [ - "created_at" + "created_at", + "registration_token_required" ] }, "SingleResponse_for_UpstreamOAuthProvider": { diff --git a/docs/config.schema.json b/docs/config.schema.json index f6d947e48..d32daa27c 100644 --- a/docs/config.schema.json +++ b/docs/config.schema.json @@ -2330,6 +2330,11 @@ "$ref": "#/definitions/OnBackchannelLogout" } ] + }, + "registration_token_required": { + "description": "Whether or not to require a registration token on `OAuth2` auth\n\n Defaults to `false`", + "type": "boolean", + "default": false } }, "required": [ @@ -2807,7 +2812,7 @@ "description": "Whether users can log in with their email address. Defaults to `false`.\n\n This has no effect if password login is disabled.", "type": "boolean" }, - "registration_token_required": { + "password_registration_token_required": { "description": "Whether registration tokens are required for password registrations.\n Defaults to `false`.\n\n When enabled, users must provide a valid registration token during\n password registration. This has no effect if password registration\n is disabled.", "type": "boolean" } From ef411fdfda990483a67ac49e505310dee2b129cc Mon Sep 17 00:00:00 2001 From: defaultdino Date: Fri, 3 Apr 2026 19:32:45 +0200 Subject: [PATCH 04/11] ignore clippy warning on config struct --- crates/config/src/sections/upstream_oauth2.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/config/src/sections/upstream_oauth2.rs b/crates/config/src/sections/upstream_oauth2.rs index 29e5d8704..b0574f760 100644 --- a/crates/config/src/sections/upstream_oauth2.rs +++ b/crates/config/src/sections/upstream_oauth2.rs @@ -513,6 +513,7 @@ impl OnBackchannelLogout { #[serde_as] #[skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[allow(clippy::struct_excessive_bools)] pub struct Provider { /// Whether this provider is enabled. /// From 32b16f90e43b8eeedfef8897cc1662312db42fab Mon Sep 17 00:00:00 2001 From: defaultdino Date: Fri, 3 Apr 2026 19:36:32 +0200 Subject: [PATCH 05/11] add registration_token_required to all upstream oauth api responses --- .../admin/v1/upstream_oauth_providers/get.rs | 3 +- .../admin/v1/upstream_oauth_providers/list.rs | 42 ++++++++++++------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs b/crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs index 410d3d279..caac6aa3a 100644 --- a/crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs +++ b/crates/handlers/src/admin/v1/upstream_oauth_providers/get.rs @@ -165,7 +165,8 @@ mod tests { "human_name": "Google", "brand_name": "google", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG0MZAA6S4AF7CTV32E" diff --git a/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs b/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs index 69f57e95c..01f0a60f3 100644 --- a/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs +++ b/crates/handlers/src/admin/v1/upstream_oauth_providers/list.rs @@ -307,7 +307,8 @@ mod tests { "human_name": "Apple ID", "brand_name": "apple", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": "2022-01-16T14:40:00Z" + "disabled_at": "2022-01-16T14:40:00Z", + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG07HNEZXNQM2KNBNF6" @@ -326,7 +327,8 @@ mod tests { "human_name": "Microsoft", "brand_name": "microsoft", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG09AVTNSQFMSR34AJC" @@ -345,7 +347,8 @@ mod tests { "human_name": "Google", "brand_name": "google", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG0MZAA6S4AF7CTV32E" @@ -395,7 +398,8 @@ mod tests { "human_name": "Microsoft", "brand_name": "microsoft", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG09AVTNSQFMSR34AJC" @@ -414,7 +418,8 @@ mod tests { "human_name": "Google", "brand_name": "google", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG0MZAA6S4AF7CTV32E" @@ -464,7 +469,8 @@ mod tests { "human_name": "Apple ID", "brand_name": "apple", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": "2022-01-16T14:40:00Z" + "disabled_at": "2022-01-16T14:40:00Z", + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG07HNEZXNQM2KNBNF6" @@ -515,7 +521,8 @@ mod tests { "human_name": "Apple ID", "brand_name": "apple", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": "2022-01-16T14:40:00Z" + "disabled_at": "2022-01-16T14:40:00Z", + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG07HNEZXNQM2KNBNF6" @@ -534,7 +541,8 @@ mod tests { "human_name": "Microsoft", "brand_name": "microsoft", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG09AVTNSQFMSR34AJC" @@ -581,7 +589,8 @@ mod tests { "human_name": "Google", "brand_name": "google", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG0MZAA6S4AF7CTV32E" @@ -643,7 +652,8 @@ mod tests { "human_name": "Apple ID", "brand_name": "apple", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": "2022-01-16T14:40:00Z" + "disabled_at": "2022-01-16T14:40:00Z", + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG07HNEZXNQM2KNBNF6" @@ -662,7 +672,8 @@ mod tests { "human_name": "Microsoft", "brand_name": "microsoft", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG09AVTNSQFMSR34AJC" @@ -681,7 +692,8 @@ mod tests { "human_name": "Google", "brand_name": "google", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG0MZAA6S4AF7CTV32E" @@ -740,7 +752,8 @@ mod tests { "human_name": "Microsoft", "brand_name": "microsoft", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG09AVTNSQFMSR34AJC" @@ -759,7 +772,8 @@ mod tests { "human_name": "Google", "brand_name": "google", "created_at": "2022-01-16T14:40:00Z", - "disabled_at": null + "disabled_at": null, + "registration_token_required": false }, "links": { "self": "/api/admin/v1/upstream-oauth-providers/01FSHN9AG0MZAA6S4AF7CTV32E" From bed56118831325f3ec79a3d8304616f381646dcd Mon Sep 17 00:00:00 2001 From: defaultdino Date: Fri, 3 Apr 2026 20:53:27 +0200 Subject: [PATCH 06/11] update syn2mas snapshot for registration_token_required column --- ...mas_writer__test__write_user_with_upstream_provider_link.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/syn2mas/src/mas_writer/snapshots/syn2mas__mas_writer__test__write_user_with_upstream_provider_link.snap b/crates/syn2mas/src/mas_writer/snapshots/syn2mas__mas_writer__test__write_user_with_upstream_provider_link.snap index 7b9173eb3..ace3b5343 100644 --- a/crates/syn2mas/src/mas_writer/snapshots/syn2mas__mas_writer__test__write_user_with_upstream_provider_link.snap +++ b/crates/syn2mas/src/mas_writer/snapshots/syn2mas__mas_writer__test__write_user_with_upstream_provider_link.snap @@ -27,6 +27,7 @@ upstream_oauth_providers: jwks_uri_override: ~ on_backchannel_logout: do_nothing pkce_mode: auto + registration_token_required: "false" response_mode: query scope: openid token_endpoint_auth_method: client_secret_basic From 3f57a9c8c730d61b41b3b3daafca8e0404869f5c Mon Sep 17 00:00:00 2001 From: defaultdino Date: Wed, 13 May 2026 13:17:51 +0200 Subject: [PATCH 07/11] resolve comments related to deprecation and transitioning --- crates/cli/src/util.rs | 9 +++++ crates/config/src/sections/account.rs | 34 +++++++++++-------- crates/data-model/src/site_config.rs | 4 +++ crates/handlers/src/admin/v1/site_config.rs | 11 +++++- .../src/views/register/steps/finish.rs | 16 ++------- ..._providers_registration_token_required.sql | 5 +++ 6 files changed, 50 insertions(+), 29 deletions(-) diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index 6d3d0ba52..fd8f358d1 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -212,6 +212,14 @@ pub fn site_config_from_config( user_session_inactivity_ttl: c.expire_user_sessions.then_some(c.ttl), }); + if account_config.registration_token_required { + tracing::warn!( + "`account.registration_token_required` is deprecated. use \ + `account.password_registration_token_required` and per-provider \ + `registration_token_required` instead" + ) + } + Ok(SiteConfig { access_token_ttl: experimental_config.access_token_ttl, compat_token_ttl: experimental_config.compat_token_ttl, @@ -224,6 +232,7 @@ pub fn site_config_from_config( && account_config.password_registration_enabled, password_registration_email_required: account_config.password_registration_email_required, password_registration_token_required: account_config.password_registration_token_required, + registration_token_required: account_config.registration_token_required, email_change_allowed: account_config.email_change_allowed, displayname_change_allowed: account_config.displayname_change_allowed, password_change_allowed: password_config.enabled() diff --git a/crates/config/src/sections/account.rs b/crates/config/src/sections/account.rs index a861e713d..dae74c583 100644 --- a/crates/config/src/sections/account.rs +++ b/crates/config/src/sections/account.rs @@ -68,7 +68,16 @@ pub struct AccountConfig { /// This has no effect if password login is disabled. #[serde(default = "default_false", skip_serializing_if = "is_default_false")] pub password_recovery_enabled: bool, - + + /// Whether registration tokens are required for password registrations. + /// Defaults to `false`. + /// + /// When enabled, users must provide a valid registration token during + /// password registration. This has no effect if password registration + /// is disabled. + #[serde(default = "default_false", skip_serializing_if = "is_default_false")] + pub password_registration_token_required: bool, + /// Whether users are allowed to delete their own account. Defaults to /// `true`. #[serde(default = "default_true", skip_serializing_if = "is_default_true")] @@ -80,18 +89,11 @@ pub struct AccountConfig { #[serde(default = "default_false", skip_serializing_if = "is_default_false")] pub login_with_email_allowed: bool, - /// Whether registration tokens are required for password registrations. - /// Defaults to `false`. - /// - /// When enabled, users must provide a valid registration token during - /// password registration. This has no effect if password registration - /// is disabled. - #[serde( - default = "default_false", - skip_serializing_if = "is_default_false", - alias = "registration_token_required" - )] - pub password_registration_token_required: bool, + /// Whether registration tokens are required for password registrations + /// This is deprecated in favor of `password_registration_token_required` + #[serde(default = "default_false", skip_serializing_if = "is_default_false")] + pub registration_token_required: bool, + } impl Default for AccountConfig { @@ -103,9 +105,10 @@ impl Default for AccountConfig { password_registration_email_required: default_true(), password_change_allowed: default_true(), password_recovery_enabled: default_false(), + password_registration_token_required: default_false(), + registration_token_required: default_false(), account_deactivation_allowed: default_true(), login_with_email_allowed: default_false(), - password_registration_token_required: default_false(), } } } @@ -118,9 +121,10 @@ impl AccountConfig { && is_default_true(&self.displayname_change_allowed) && is_default_true(&self.password_change_allowed) && is_default_false(&self.password_recovery_enabled) + && is_default_false(&self.password_registration_token_required) + && is_default_false(&self.registration_token_required) && is_default_true(&self.account_deactivation_allowed) && is_default_false(&self.login_with_email_allowed) - && is_default_false(&self.password_registration_token_required) } } diff --git a/crates/data-model/src/site_config.rs b/crates/data-model/src/site_config.rs index 7adb683ed..ab4e85c51 100644 --- a/crates/data-model/src/site_config.rs +++ b/crates/data-model/src/site_config.rs @@ -79,6 +79,10 @@ pub struct SiteConfig { /// Whether registration tokens are required for password registrations. pub password_registration_token_required: bool, + /// Whether registration tokens are required globally for password registrations + /// Deprecated in favor of `password_registration_token_required` + pub registration_token_required: bool, + /// Whether users can change their email. pub email_change_allowed: bool, diff --git a/crates/handlers/src/admin/v1/site_config.rs b/crates/handlers/src/admin/v1/site_config.rs index 17e95e5c5..51766116d 100644 --- a/crates/handlers/src/admin/v1/site_config.rs +++ b/crates/handlers/src/admin/v1/site_config.rs @@ -25,9 +25,14 @@ pub struct SiteConfig { /// Whether a valid email address is required for password registrations. pub password_registration_email_required: bool, - /// Whether registration tokens are required for password registrations. pub password_registration_token_required: bool, + /// Whether registration tokens are required for password registrations. + /// Deprecated in favor of `password_registration_token_required` + #[deprecated = "use `password_registration_token_required` instead"] + #[allow(deprecated)] + pub registration_token_required: bool, + /// Whether users can change their email. pub email_change_allowed: bool, @@ -52,6 +57,7 @@ pub struct SiteConfig { pub minimum_password_complexity: u8, } +#[allow(deprecated)] pub fn doc(operation: TransformOperation) -> TransformOperation { operation .id("siteConfig") @@ -64,6 +70,7 @@ pub fn doc(operation: TransformOperation) -> TransformOperation { password_registration_enabled: true, password_registration_email_required: true, password_registration_token_required: true, + registration_token_required: true, email_change_allowed: true, displayname_change_allowed: true, password_change_allowed: true, @@ -76,6 +83,7 @@ pub fn doc(operation: TransformOperation) -> TransformOperation { } #[tracing::instrument(name = "handler.admin.v1.site_config", skip_all)] +#[allow(deprecated)] pub async fn handler( _: CallContext, State(site_config): State, @@ -86,6 +94,7 @@ pub async fn handler( password_registration_enabled: site_config.password_registration_enabled, password_registration_email_required: site_config.password_registration_email_required, password_registration_token_required: site_config.password_registration_token_required, + registration_token_required: site_config.password_registration_token_required, email_change_allowed: site_config.email_change_allowed, displayname_change_allowed: site_config.displayname_change_allowed, password_change_allowed: site_config.password_change_allowed, diff --git a/crates/handlers/src/views/register/steps/finish.rs b/crates/handlers/src/views/register/steps/finish.rs index 697891a49..792ad478e 100644 --- a/crates/handlers/src/views/register/steps/finish.rs +++ b/crates/handlers/src/views/register/steps/finish.rs @@ -120,17 +120,7 @@ pub(crate) async fn get( ))); } - let token_required = if registration - .upstream_oauth_authorization_session_id - .is_some() - { - // load provider to let OAuth registrations check its - // per provider `registration_token_required` flag - let session_id = registration - .upstream_oauth_authorization_session_id - .context("Missing upstream OAuth session ID") - .map_err(InternalError::from_anyhow)?; - + let token_required = if let Some(session_id) = registration.upstream_oauth_authorization_session_id { let session = repo .upstream_oauth_session() .lookup(session_id) @@ -145,9 +135,9 @@ pub(crate) async fn get( .context("Could not load the upstream OAuth provider") .map_err(InternalError::from_anyhow)?; - provider.registration_token_required + provider.registration_token_required || site_config.registration_token_required } else { - site_config.password_registration_token_required + site_config.password_registration_token_required || site_config.registration_token_required }; let registration_token = if token_required { diff --git a/crates/storage-pg/migrations/20260403144138_upstream_oauth2_providers_registration_token_required.sql b/crates/storage-pg/migrations/20260403144138_upstream_oauth2_providers_registration_token_required.sql index d1c35003b..7570b5c7d 100644 --- a/crates/storage-pg/migrations/20260403144138_upstream_oauth2_providers_registration_token_required.sql +++ b/crates/storage-pg/migrations/20260403144138_upstream_oauth2_providers_registration_token_required.sql @@ -1,3 +1,8 @@ +-- Copyright 2026 Element Creations Ltd. +-- +-- SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +-- Please see LICENSE files in the repository root for full details. + -- Adds a `registration_token_required` column to the UpstreamOauthProvider table ALTER TABLE upstream_oauth_providers From e25f967a66dd71fc3987d7044e92e4c0216a2f18 Mon Sep 17 00:00:00 2001 From: defaultdino Date: Wed, 13 May 2026 13:18:22 +0200 Subject: [PATCH 08/11] format --- crates/config/src/sections/account.rs | 5 ++--- crates/handlers/src/views/register/steps/finish.rs | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/config/src/sections/account.rs b/crates/config/src/sections/account.rs index dae74c583..d1e7e2f1b 100644 --- a/crates/config/src/sections/account.rs +++ b/crates/config/src/sections/account.rs @@ -68,7 +68,7 @@ pub struct AccountConfig { /// This has no effect if password login is disabled. #[serde(default = "default_false", skip_serializing_if = "is_default_false")] pub password_recovery_enabled: bool, - + /// Whether registration tokens are required for password registrations. /// Defaults to `false`. /// @@ -77,7 +77,7 @@ pub struct AccountConfig { /// is disabled. #[serde(default = "default_false", skip_serializing_if = "is_default_false")] pub password_registration_token_required: bool, - + /// Whether users are allowed to delete their own account. Defaults to /// `true`. #[serde(default = "default_true", skip_serializing_if = "is_default_true")] @@ -93,7 +93,6 @@ pub struct AccountConfig { /// This is deprecated in favor of `password_registration_token_required` #[serde(default = "default_false", skip_serializing_if = "is_default_false")] pub registration_token_required: bool, - } impl Default for AccountConfig { diff --git a/crates/handlers/src/views/register/steps/finish.rs b/crates/handlers/src/views/register/steps/finish.rs index 792ad478e..a6c699ac6 100644 --- a/crates/handlers/src/views/register/steps/finish.rs +++ b/crates/handlers/src/views/register/steps/finish.rs @@ -120,7 +120,9 @@ pub(crate) async fn get( ))); } - let token_required = if let Some(session_id) = registration.upstream_oauth_authorization_session_id { + let token_required = if let Some(session_id) = + registration.upstream_oauth_authorization_session_id + { let session = repo .upstream_oauth_session() .lookup(session_id) From ba724436273e5dc17921c27278b62713b0f9c12c Mon Sep 17 00:00:00 2001 From: defaultdino Date: Wed, 13 May 2026 13:27:23 +0200 Subject: [PATCH 09/11] fix style, run update.sh and fix test for SiteConfig --- crates/data-model/src/site_config.rs | 5 +++-- crates/handlers/src/test_utils.rs | 1 + docs/api/spec.json | 8 +++++++- docs/config.schema.json | 8 ++++++-- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/crates/data-model/src/site_config.rs b/crates/data-model/src/site_config.rs index ab4e85c51..2f3c17d25 100644 --- a/crates/data-model/src/site_config.rs +++ b/crates/data-model/src/site_config.rs @@ -79,8 +79,9 @@ pub struct SiteConfig { /// Whether registration tokens are required for password registrations. pub password_registration_token_required: bool, - /// Whether registration tokens are required globally for password registrations - /// Deprecated in favor of `password_registration_token_required` + /// Whether registration tokens are required globally for password + /// registrations. Deprecated in favor of + /// `password_registration_token_required` pub registration_token_required: bool, /// Whether users can change their email. diff --git a/crates/handlers/src/test_utils.rs b/crates/handlers/src/test_utils.rs index c72a7d56e..6d6258cb6 100644 --- a/crates/handlers/src/test_utils.rs +++ b/crates/handlers/src/test_utils.rs @@ -138,6 +138,7 @@ pub fn test_site_config() -> SiteConfig { password_login_enabled: true, password_registration_enabled: true, password_registration_token_required: false, + registration_token_required: false, email_change_allowed: true, displayname_change_allowed: true, password_change_allowed: true, diff --git a/docs/api/spec.json b/docs/api/spec.json index ace81d912..f234b4c61 100644 --- a/docs/api/spec.json +++ b/docs/api/spec.json @@ -37,6 +37,7 @@ "password_registration_enabled": true, "password_registration_email_required": true, "password_registration_token_required": true, + "registration_token_required": true, "email_change_allowed": true, "displayname_change_allowed": true, "password_change_allowed": true, @@ -4864,9 +4865,13 @@ "type": "boolean" }, "password_registration_token_required": { - "description": "Whether registration tokens are required for password registrations.", "type": "boolean" }, + "registration_token_required": { + "description": "Whether registration tokens are required for password registrations.\n Deprecated in favor of `password_registration_token_required`", + "type": "boolean", + "deprecated": true + }, "email_change_allowed": { "description": "Whether users can change their email.", "type": "boolean" @@ -4905,6 +4910,7 @@ "password_registration_enabled", "password_registration_email_required", "password_registration_token_required", + "registration_token_required", "email_change_allowed", "displayname_change_allowed", "password_change_allowed", diff --git a/docs/config.schema.json b/docs/config.schema.json index d32daa27c..139c2a7ff 100644 --- a/docs/config.schema.json +++ b/docs/config.schema.json @@ -2804,6 +2804,10 @@ "description": "Whether email-based password recovery is enabled. Defaults to `false`.\n\n This has no effect if password login is disabled.", "type": "boolean" }, + "password_registration_token_required": { + "description": "Whether registration tokens are required for password registrations.\n Defaults to `false`.\n\n When enabled, users must provide a valid registration token during\n password registration. This has no effect if password registration\n is disabled.", + "type": "boolean" + }, "account_deactivation_allowed": { "description": "Whether users are allowed to delete their own account. Defaults to\n `true`.", "type": "boolean" @@ -2812,8 +2816,8 @@ "description": "Whether users can log in with their email address. Defaults to `false`.\n\n This has no effect if password login is disabled.", "type": "boolean" }, - "password_registration_token_required": { - "description": "Whether registration tokens are required for password registrations.\n Defaults to `false`.\n\n When enabled, users must provide a valid registration token during\n password registration. This has no effect if password registration\n is disabled.", + "registration_token_required": { + "description": "Whether registration tokens are required for password registrations\n This is deprecated in favor of `password_registration_token_required`", "type": "boolean" } } From 5eae746361b1575e82954ea1631ed96dfb0c4323 Mon Sep 17 00:00:00 2001 From: defaultdino Date: Wed, 13 May 2026 13:30:48 +0200 Subject: [PATCH 10/11] format --- crates/data-model/src/site_config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/data-model/src/site_config.rs b/crates/data-model/src/site_config.rs index 2f3c17d25..1320a2780 100644 --- a/crates/data-model/src/site_config.rs +++ b/crates/data-model/src/site_config.rs @@ -79,8 +79,8 @@ pub struct SiteConfig { /// Whether registration tokens are required for password registrations. pub password_registration_token_required: bool, - /// Whether registration tokens are required globally for password - /// registrations. Deprecated in favor of + /// Whether registration tokens are required globally for password + /// registrations. Deprecated in favor of /// `password_registration_token_required` pub registration_token_required: bool, From 58aeb6add078334a79f13d3f699f447efc4870b0 Mon Sep 17 00:00:00 2001 From: defaultdino Date: Wed, 13 May 2026 13:43:18 +0200 Subject: [PATCH 11/11] fix clippy issue --- crates/cli/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index fd8f358d1..bcba8355a 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -217,7 +217,7 @@ pub fn site_config_from_config( "`account.registration_token_required` is deprecated. use \ `account.password_registration_token_required` and per-provider \ `registration_token_required` instead" - ) + ); } Ok(SiteConfig {