mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-08-14 06:59:46 +00:00
config: align section docs and examples with the configuration reference
Enriches every config section's doc comments, schemars examples and x-doc rendering hints so that the generated reference matches (and in places improves on) the hand-written document: previously-undocumented fields are now documented, rustdoc-only idioms that leaked into the schema are gone, and a handful of typos are fixed. Metadata only - no runtime behavior change.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -27,71 +28,88 @@ const fn is_default_false(value: &bool) -> bool {
|
||||
*value == default_false()
|
||||
}
|
||||
|
||||
/// Configuration section to configure features related to account management
|
||||
/// Configuration related to account management
|
||||
#[expect(clippy::struct_excessive_bools)]
|
||||
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
|
||||
pub struct AccountConfig {
|
||||
/// Whether users are allowed to change their email addresses. Defaults to
|
||||
/// `true`.
|
||||
/// Whether users are allowed to change their email addresses.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
#[serde(default = "default_true", skip_serializing_if = "is_default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub email_change_allowed: bool,
|
||||
|
||||
/// Whether users are allowed to change their display names. Defaults to
|
||||
/// `true`.
|
||||
/// Whether users are allowed to change their display names.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
/// This should be in sync with the policy in the homeserver configuration.
|
||||
#[serde(default = "default_true", skip_serializing_if = "is_default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub displayname_change_allowed: bool,
|
||||
|
||||
/// Whether to enable self-service password registration. Defaults to
|
||||
/// `false` if password authentication is enabled.
|
||||
/// Whether to enable self-service password registration.
|
||||
///
|
||||
/// Defaults to `false`.
|
||||
/// This has no effect if password login is disabled.
|
||||
#[serde(default = "default_false", skip_serializing_if = "is_default_false")]
|
||||
#[schemars(example = &false)]
|
||||
pub password_registration_enabled: bool,
|
||||
|
||||
/// Whether self-service password registrations require a valid email.
|
||||
/// Defaults to `true`.
|
||||
/// Whether self-service registrations require a valid email.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
/// This has no effect if password registration is disabled.
|
||||
#[serde(default = "default_true", skip_serializing_if = "is_default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub password_registration_email_required: bool,
|
||||
|
||||
/// Whether users are allowed to change their passwords. Defaults to `true`.
|
||||
/// Whether users are allowed to change their passwords.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
/// This has no effect if password login is disabled.
|
||||
#[serde(default = "default_true", skip_serializing_if = "is_default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub password_change_allowed: bool,
|
||||
|
||||
/// Whether email-based password recovery is enabled. Defaults to `false`.
|
||||
/// Whether email-based password recovery is enabled.
|
||||
///
|
||||
/// Defaults to `false`.
|
||||
/// This has no effect if password login is disabled.
|
||||
#[serde(default = "default_false", skip_serializing_if = "is_default_false")]
|
||||
#[schemars(example = &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.
|
||||
/// password registration. This has no effect if password registration is
|
||||
/// disabled.
|
||||
#[serde(default = "default_false", skip_serializing_if = "is_default_false")]
|
||||
#[schemars(example = &false)]
|
||||
pub password_registration_token_required: bool,
|
||||
|
||||
/// Whether users are allowed to delete their own account. Defaults to
|
||||
/// `true`.
|
||||
/// Whether users are allowed to delete their own account.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
#[serde(default = "default_true", skip_serializing_if = "is_default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub account_deactivation_allowed: bool,
|
||||
|
||||
/// Whether users can log in with their email address. Defaults to `false`.
|
||||
/// Whether users can log in with their email address.
|
||||
///
|
||||
/// Defaults to `false`.
|
||||
/// This has no effect if password login is disabled.
|
||||
#[serde(default = "default_false", skip_serializing_if = "is_default_false")]
|
||||
#[schemars(example = &false)]
|
||||
pub login_with_email_allowed: bool,
|
||||
|
||||
/// Whether registration tokens are required for password registrations
|
||||
/// This is deprecated in favor of `password_registration_token_required`
|
||||
/// Whether registration tokens are required for password registrations.
|
||||
///
|
||||
/// Deprecated: use `password_registration_token_required` instead.
|
||||
#[serde(default = "default_false", skip_serializing_if = "is_default_false")]
|
||||
#[schemars(example = &false)]
|
||||
pub registration_token_required: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -11,28 +11,36 @@ use url::Url;
|
||||
|
||||
use crate::ConfigurationSection;
|
||||
|
||||
/// Configuration section for tweaking the branding of the service
|
||||
/// Configuration section for tweaking the branding of the service.
|
||||
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, Default)]
|
||||
pub struct BrandingConfig {
|
||||
/// A human-readable name. Defaults to the server's address.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub service_name: Option<String>,
|
||||
|
||||
/// Link to a privacy policy, displayed in the footer of web pages and
|
||||
/// emails. It is also advertised to clients through the `op_policy_uri`
|
||||
/// OIDC provider metadata.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub policy_uri: Option<Url>,
|
||||
|
||||
/// Link to a terms of service document, displayed in the footer of web
|
||||
/// pages and emails. It is also advertised to clients through the
|
||||
/// `op_tos_uri` OIDC provider metadata.
|
||||
///
|
||||
/// This also adds a mandatory checkbox during registration. The value of
|
||||
/// this config item will be stored in the `user_terms` table to indicate
|
||||
/// which `ToS` document the user accepted. Note that currently changing
|
||||
/// this value will not force existing users to re-accept terms.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub tos_uri: Option<Url>,
|
||||
|
||||
/// Legal imprint, displayed in the footer in the footer of web pages and
|
||||
/// emails.
|
||||
/// Legal imprint, displayed in the footer of web pages and emails.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub imprint: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -12,7 +13,7 @@ use crate::ConfigurationSection;
|
||||
/// Which service should be used for CAPTCHA protection
|
||||
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
|
||||
pub enum CaptchaServiceKind {
|
||||
/// Use Google's reCAPTCHA v2 API
|
||||
/// Use Google's `reCAPTCHA` v2 API
|
||||
#[serde(rename = "recaptcha_v2")]
|
||||
RecaptchaV2,
|
||||
|
||||
@@ -20,24 +21,34 @@ pub enum CaptchaServiceKind {
|
||||
#[serde(rename = "cloudflare_turnstile")]
|
||||
CloudflareTurnstile,
|
||||
|
||||
/// Use ``HCaptcha``
|
||||
/// Use `hCaptcha`
|
||||
#[serde(rename = "hcaptcha")]
|
||||
HCaptcha,
|
||||
}
|
||||
|
||||
/// Configuration section to setup CAPTCHA protection on a few operations
|
||||
/// Settings related to CAPTCHA protection
|
||||
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, Default)]
|
||||
pub struct CaptchaConfig {
|
||||
/// Which service should be used for CAPTCHA protection
|
||||
/// Which service should be used for CAPTCHA protection. Set to `null` (or
|
||||
/// `~`) to disable CAPTCHA protection
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &Option::<CaptchaServiceKind>::None)]
|
||||
pub service: Option<CaptchaServiceKind>,
|
||||
|
||||
/// The site key to use
|
||||
/// The site key to use.
|
||||
///
|
||||
/// The expected value depends on the chosen `service`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub site_key: Option<String>,
|
||||
|
||||
/// The secret key to use
|
||||
/// The secret key to use.
|
||||
///
|
||||
/// The expected value depends on the chosen `service`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub secret_key: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -21,23 +21,21 @@ use super::{ClientSecret, ClientSecretRaw, ConfigurationSection};
|
||||
#[derive(JsonSchema, Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ClientAuthMethodConfig {
|
||||
/// `none`: No authentication
|
||||
/// No authentication
|
||||
None,
|
||||
|
||||
/// `client_secret_basic`: `client_id` and `client_secret` used as basic
|
||||
/// authorization credentials
|
||||
/// `client_id` and `client_secret` used as basic authorization credentials
|
||||
ClientSecretBasic,
|
||||
|
||||
/// `client_secret_post`: `client_id` and `client_secret` sent in the
|
||||
/// request body
|
||||
/// `client_id` and `client_secret` sent in the request body
|
||||
ClientSecretPost,
|
||||
|
||||
/// `client_secret_basic`: a `client_assertion` sent in the request body and
|
||||
/// signed using the `client_secret`
|
||||
/// A `client_assertion` sent in the request body and signed using the
|
||||
/// `client_secret`
|
||||
ClientSecretJwt,
|
||||
|
||||
/// `client_secret_basic`: a `client_assertion` sent in the request body and
|
||||
/// signed by an asymmetric key
|
||||
/// A `client_assertion` sent in the request body and signed by an
|
||||
/// asymmetric key
|
||||
PrivateKeyJwt,
|
||||
}
|
||||
|
||||
@@ -61,15 +59,19 @@ pub struct ClientConfig {
|
||||
#[schemars(
|
||||
with = "String",
|
||||
regex(pattern = r"^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$"),
|
||||
description = "A ULID as per https://github.com/ulid/spec"
|
||||
description = "A ULID as per https://github.com/ulid/spec",
|
||||
example = &"000000000000000000000FIRST"
|
||||
)]
|
||||
pub client_id: Ulid,
|
||||
|
||||
/// Authentication method used for this client
|
||||
#[schemars(example = &"client_secret_post")]
|
||||
client_auth_method: ClientAuthMethodConfig,
|
||||
|
||||
/// Name of the `OAuth2` client
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"My Application")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub client_name: Option<String>,
|
||||
|
||||
/// Client URL for user-facing information about the client
|
||||
@@ -86,15 +88,29 @@ pub struct ClientConfig {
|
||||
/// The JSON Web Key Set (JWKS) used by the `private_key_jwt` authentication
|
||||
/// method. Mutually exclusive with `jwks_uri`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &serde_json::json!({
|
||||
"keys": [{
|
||||
"kid": "03e84aed4ef4431014e8617567864c4efaaaede9",
|
||||
"kty": "RSA",
|
||||
"alg": "RS256",
|
||||
"use": "sig",
|
||||
"e": "AQAB",
|
||||
"n": "<base64url-encoded modulus>"
|
||||
}]
|
||||
}))]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub jwks: Option<PublicJsonWebKeySet>,
|
||||
|
||||
/// The URL of the JSON Web Key Set (JWKS) used by the `private_key_jwt`
|
||||
/// authentication method. Mutually exclusive with `jwks`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"https://example.com/.well-known/jwks.json")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub jwks_uri: Option<Url>,
|
||||
|
||||
/// List of allowed redirect URIs
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[schemars(example = &["https://example.com/callback"])]
|
||||
pub redirect_uris: Vec<Url>,
|
||||
}
|
||||
|
||||
@@ -209,7 +225,15 @@ impl ClientConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// List of OAuth 2.0/OIDC clients config
|
||||
/// List of OAuth 2.0/OIDC clients and their keys/secrets. Each `client_id` must
|
||||
/// be a [ULID](https://github.com/ulid/spec).
|
||||
///
|
||||
/// <!-- more -->
|
||||
///
|
||||
/// **Note:** any additions or modifications in this list are synced with the
|
||||
/// database on server startup. Removed entries are only removed with the
|
||||
/// [`config sync --prune`](./cli/config.md#config-sync---prune---dry-run)
|
||||
/// command.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(transparent)]
|
||||
pub struct ClientsConfig(#[schemars(with = "Vec::<ClientConfig>")] Vec<ClientConfig>);
|
||||
|
||||
@@ -80,7 +80,7 @@ pub enum PgSslMode {
|
||||
Prefer,
|
||||
|
||||
/// Only try an SSL connection. If a root CA file is present, verify the
|
||||
/// connection in the same way as if `VerifyCa` was specified.
|
||||
/// connection in the same way as if `verify-ca` was specified.
|
||||
Require,
|
||||
|
||||
/// Only try an SSL connection, and verify that the server certificate is
|
||||
@@ -93,30 +93,42 @@ pub enum PgSslMode {
|
||||
VerifyFull,
|
||||
}
|
||||
|
||||
/// Database connection configuration
|
||||
/// Configure how to connect to the PostgreSQL database.
|
||||
///
|
||||
/// MAS must not be connected to a database pooler (such as pgBouncer or pgCat)
|
||||
/// when it is configured in transaction pooling mode.
|
||||
/// See [the relevant section of the database
|
||||
/// page](./database.md#a-warning-about-database-pooling-software) for more
|
||||
/// information.
|
||||
#[serde_as]
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct DatabaseConfig {
|
||||
/// Connection URI
|
||||
/// Full connection string as per
|
||||
/// <https://www.postgresql.org/docs/13/libpq-connect.html#id-1.7.3.8.3.6>
|
||||
///
|
||||
/// This must not be specified if `host`, `port`, `socket`, `username`,
|
||||
/// `password`, or `database` are specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(url, default = "default_connection_string")]
|
||||
#[schemars(url, default = "default_connection_string", example = &"postgresql://user:password@hostname:5432/database?sslmode=require")]
|
||||
pub uri: Option<String>,
|
||||
|
||||
/// Name of host to connect to
|
||||
/// Alternatively, the connection can be configured with separate
|
||||
/// parameters.
|
||||
///
|
||||
/// Name of host to connect to.
|
||||
///
|
||||
/// This must not be specified if `uri` is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option::<schema::Hostname>")]
|
||||
#[schemars(with = "Option::<schema::Hostname>", example = &"hostname")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub host: Option<String>,
|
||||
|
||||
/// Port number to connect at the server host
|
||||
///
|
||||
/// This must not be specified if `uri` is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(range(min = 1, max = 65535))]
|
||||
#[schemars(range(min = 1, max = 65535), example = &5432u16)]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub port: Option<u16>,
|
||||
|
||||
/// Directory containing the UNIX socket to connect to
|
||||
@@ -124,18 +136,23 @@ pub struct DatabaseConfig {
|
||||
/// This must not be specified if `uri` is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub socket: Option<Utf8PathBuf>,
|
||||
|
||||
/// PostgreSQL user name to connect as
|
||||
///
|
||||
/// This must not be specified if `uri` is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"user")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub username: Option<String>,
|
||||
|
||||
/// Password to be used if the server demands password authentication
|
||||
///
|
||||
/// This must not be specified if `uri` is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"password")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub password: Option<String>,
|
||||
|
||||
/// Path to the password to be used if the server demands password
|
||||
@@ -151,68 +168,78 @@ pub struct DatabaseConfig {
|
||||
///
|
||||
/// This must not be specified if `uri` is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"database")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub database: Option<String>,
|
||||
|
||||
/// How to handle SSL connections
|
||||
/// Whether to use SSL to connect to the database
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &PgSslMode::Require)]
|
||||
pub ssl_mode: Option<PgSslMode>,
|
||||
|
||||
/// The PEM-encoded root certificate for SSL connections
|
||||
///
|
||||
/// This must not be specified if the `ssl_ca_file` option is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub ssl_ca: Option<String>,
|
||||
|
||||
/// Path to the root certificate for SSL connections
|
||||
///
|
||||
/// This must not be specified if the `ssl_ca` option is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/ca.pem")]
|
||||
pub ssl_ca_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// The PEM-encoded client certificate for SSL connections
|
||||
/// Client certificate to present to the server when SSL is enabled.
|
||||
///
|
||||
/// The PEM-encoded client certificate for SSL connections.
|
||||
///
|
||||
/// This must not be specified if the `ssl_certificate_file` option is
|
||||
/// specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub ssl_certificate: Option<String>,
|
||||
|
||||
/// Path to the client certificate for SSL connections
|
||||
///
|
||||
/// This must not be specified if the `ssl_certificate` option is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/cert.pem")]
|
||||
pub ssl_certificate_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// The PEM-encoded client key for SSL connections
|
||||
///
|
||||
/// This must not be specified if the `ssl_key_file` option is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub ssl_key: Option<String>,
|
||||
|
||||
/// Path to the client key for SSL connections
|
||||
///
|
||||
/// This must not be specified if the `ssl_key` option is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/key.pem")]
|
||||
pub ssl_key_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// Set the maximum number of connections the pool should maintain
|
||||
#[serde(default = "default_max_connections")]
|
||||
#[schemars(example = &10u32)]
|
||||
pub max_connections: NonZeroU32,
|
||||
|
||||
/// Set the minimum number of connections the pool should maintain
|
||||
#[serde(default)]
|
||||
#[schemars(example = &0u32)]
|
||||
pub min_connections: u32,
|
||||
|
||||
/// Set the amount of time to attempt connecting to the database
|
||||
#[schemars(with = "u64")]
|
||||
/// Set the amount of time to attempt connecting to the database, in seconds
|
||||
#[schemars(with = "u64", example = &30u64)]
|
||||
#[serde(default = "default_connect_timeout")]
|
||||
#[serde_as(as = "serde_with::DurationSeconds<u64>")]
|
||||
pub connect_timeout: Duration,
|
||||
|
||||
/// Set a maximum idle duration for individual connections
|
||||
#[schemars(with = "Option<u64>")]
|
||||
/// Set a maximum idle duration for individual connections, in seconds
|
||||
#[schemars(with = "Option<u64>", example = &600u64)]
|
||||
#[serde(
|
||||
default = "default_idle_timeout",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
@@ -220,8 +247,8 @@ pub struct DatabaseConfig {
|
||||
#[serde_as(as = "Option<serde_with::DurationSeconds<u64>>")]
|
||||
pub idle_timeout: Option<Duration>,
|
||||
|
||||
/// Set the maximum lifetime of individual connections
|
||||
#[schemars(with = "u64")]
|
||||
/// Set the maximum lifetime of individual connections, in seconds
|
||||
#[schemars(with = "u64", example = &1800u64)]
|
||||
#[serde(
|
||||
default = "default_max_lifetime",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -53,35 +54,59 @@ fn default_sendmail_command() -> Option<String> {
|
||||
Some("sendmail".to_owned())
|
||||
}
|
||||
|
||||
/// Configuration related to sending emails
|
||||
/// Hand-authored rendering of the transport selection for the documentation.
|
||||
///
|
||||
/// The schema models the transport as a flat `transport` enum plus sibling
|
||||
/// optional fields, but the reference groups them per transport, so the whole
|
||||
/// block is authored here and the sibling fields are skipped in the docs.
|
||||
const TRANSPORT_DOC_YAML: &str = r"# Default transport: don't send any emails
|
||||
transport: blackhole
|
||||
|
||||
# Send emails using SMTP
|
||||
#transport: smtp
|
||||
#mode: plain | tls | starttls
|
||||
#hostname: localhost
|
||||
#port: 587
|
||||
#username: username
|
||||
#password: password
|
||||
|
||||
# Send emails by calling a local sendmail binary
|
||||
#transport: sendmail
|
||||
#command: /usr/sbin/sendmail";
|
||||
|
||||
/// Settings related to sending emails
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct EmailConfig {
|
||||
/// Email address to use as From when sending emails
|
||||
#[serde(default = "default_email")]
|
||||
#[schemars(email)]
|
||||
#[schemars(email, example = &r#""The almighty auth service" <auth@example.com>"#)]
|
||||
pub from: String,
|
||||
|
||||
/// Email address to use as Reply-To when sending emails
|
||||
#[serde(default = "default_email")]
|
||||
#[schemars(email)]
|
||||
#[schemars(email, example = &r#""No reply" <no-reply@example.com>"#)]
|
||||
pub reply_to: String,
|
||||
|
||||
/// What backend should be used when sending emails
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "yaml": TRANSPORT_DOC_YAML })))]
|
||||
transport: EmailTransportKind,
|
||||
|
||||
/// SMTP transport: Connection mode to the relay
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
mode: Option<EmailSmtpMode>,
|
||||
|
||||
/// SMTP transport: Hostname to connect to
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<crate::schema::Hostname>")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
hostname: Option<String>,
|
||||
|
||||
/// SMTP transport: Port to connect to. Default is 25 for plain, 465 for TLS
|
||||
/// and 587 for `StartTLS`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(range(min = 1, max = 65535))]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
port: Option<NonZeroU16>,
|
||||
|
||||
/// SMTP transport: Username for use to authenticate when connecting to the
|
||||
@@ -89,6 +114,7 @@ pub struct EmailConfig {
|
||||
///
|
||||
/// Must be set if the `password` or `password_file` field is set
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
username: Option<String>,
|
||||
|
||||
/// SMTP transport: Password for use to authenticate when connecting to the
|
||||
@@ -96,6 +122,7 @@ pub struct EmailConfig {
|
||||
///
|
||||
/// Must be set if the `username` but not `password_file` field is set
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
password: Option<String>,
|
||||
|
||||
/// SMTP transport: Path to the password for use to authenticate when
|
||||
@@ -109,6 +136,7 @@ pub struct EmailConfig {
|
||||
/// Sendmail transport: Command to use to send emails
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(default = "default_sendmail_command")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
command: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -33,59 +34,71 @@ fn is_default_token_ttl(value: &Duration) -> bool {
|
||||
#[serde_as]
|
||||
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
|
||||
pub struct InactiveSessionExpirationConfig {
|
||||
/// Time after which an inactive session is automatically finished
|
||||
#[schemars(with = "u64", range(min = 600, max = 7_776_000))]
|
||||
/// Time after which an inactive session is automatically finished, in
|
||||
/// seconds
|
||||
#[schemars(with = "u64", range(min = 600, max = 7_776_000), example = &32400)]
|
||||
#[serde_as(as = "serde_with::DurationSeconds<i64>")]
|
||||
pub ttl: Duration,
|
||||
|
||||
/// Should compatibility sessions expire after inactivity
|
||||
/// Should compatibility sessions expire after inactivity. Defaults to true.
|
||||
#[serde(default = "default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub expire_compat_sessions: bool,
|
||||
|
||||
/// Should OAuth 2.0 sessions expire after inactivity
|
||||
/// Should OAuth 2.0 sessions expire after inactivity. Defaults to true.
|
||||
#[serde(default = "default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub expire_oauth_sessions: bool,
|
||||
|
||||
/// Should user sessions expire after inactivity
|
||||
/// Should user sessions expire after inactivity. Defaults to true.
|
||||
#[serde(default = "default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub expire_user_sessions: bool,
|
||||
}
|
||||
|
||||
/// Configuration sections for experimental options
|
||||
/// Settings that may change or be removed in future versions.
|
||||
/// Some of which are in this section because they don't have a stable place
|
||||
/// in the configuration yet.
|
||||
///
|
||||
/// Do not change these options unless you know what you are doing.
|
||||
#[serde_as]
|
||||
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
|
||||
pub struct ExperimentalConfig {
|
||||
/// Time-to-live of access tokens in seconds. Defaults to 5 minutes.
|
||||
#[schemars(with = "u64", range(min = 60, max = 86400))]
|
||||
/// Time-to-live of OAuth 2.0 access tokens in seconds. Defaults to 300, 5
|
||||
/// minutes.
|
||||
#[schemars(with = "u64", range(min = 60, max = 86400), example = &300)]
|
||||
#[serde(
|
||||
default = "default_token_ttl",
|
||||
skip_serializing_if = "is_default_token_ttl"
|
||||
)]
|
||||
#[serde_as(as = "serde_with::DurationSeconds<i64>")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub access_token_ttl: Duration,
|
||||
|
||||
/// Time-to-live of compatibility access tokens in seconds. Defaults to 5
|
||||
/// minutes.
|
||||
#[schemars(with = "u64", range(min = 60, max = 86400))]
|
||||
/// Time-to-live of compatibility access tokens in seconds, when refresh
|
||||
/// tokens are supported. Defaults to 300, 5 minutes.
|
||||
#[schemars(with = "u64", range(min = 60, max = 86400), example = &300)]
|
||||
#[serde(
|
||||
default = "default_token_ttl",
|
||||
skip_serializing_if = "is_default_token_ttl"
|
||||
)]
|
||||
#[serde_as(as = "serde_with::DurationSeconds<i64>")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub compat_token_ttl: Duration,
|
||||
|
||||
/// Experimetal feature to automatically expire inactive sessions
|
||||
/// Experimental feature to automatically expire inactive sessions.
|
||||
///
|
||||
/// Disabled by default
|
||||
/// Disabled by default.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub inactive_session_expiration: Option<InactiveSessionExpirationConfig>,
|
||||
|
||||
/// Experimental feature to show a plan management tab and iframe.
|
||||
/// This value is passed through "as is" to the client without any
|
||||
/// validation.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"https://example.com/plan")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub plan_management_iframe_uri: Option<String>,
|
||||
|
||||
/// Experimental feature to limit the number of application sessions per
|
||||
@@ -93,6 +106,7 @@ pub struct ExperimentalConfig {
|
||||
///
|
||||
/// Disabled by default.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub session_limit: Option<SessionLimitConfig>,
|
||||
}
|
||||
|
||||
@@ -150,20 +164,18 @@ pub struct SessionLimitConfig {
|
||||
/// This is not enforced in non-interactive contexts (like
|
||||
/// `m.login.password` login with the compatibility API) as there is no
|
||||
/// opportunity for us to show some UI for people remove some sessions.
|
||||
/// See [`hard_limit`] for enforcement on that side.
|
||||
/// See `hard_limit` for enforcement on that side.
|
||||
///
|
||||
/// This is the limit that is displayed in the UI
|
||||
///
|
||||
/// [`hard_limit`]: Self::hard_limit
|
||||
#[schemars(example = &10)]
|
||||
pub soft_limit: NonZeroU64,
|
||||
/// Upon login, when `dangerous_hard_limit_eviction: false`, will refuse the
|
||||
/// new login (policy violation error), otherwise, see
|
||||
/// [`dangerous_hard_limit_eviction`].
|
||||
/// `dangerous_hard_limit_eviction`.
|
||||
///
|
||||
/// The hard limit is enforced in all contexts
|
||||
/// (interactive/non-interactive).
|
||||
///
|
||||
/// [`dangerous_hard_limit_eviction`]: Self::dangerous_hard_limit_eviction
|
||||
#[schemars(example = &50)]
|
||||
pub hard_limit: NonZeroU64,
|
||||
/// When set, only accounts with <= `max_session_threshold` sessions have
|
||||
/// the session limits applied.
|
||||
@@ -175,9 +187,10 @@ pub struct SessionLimitConfig {
|
||||
/// and you want to avoid breaking their operation while maintaining some
|
||||
/// level of sanity with the number of devices that people can have.
|
||||
/// This will prevent anyone else from crossing the limit.
|
||||
#[schemars(example = &100)]
|
||||
pub max_session_threshold: Option<NonZeroU64>,
|
||||
/// Whether we should automatically choose the least recently used devices
|
||||
/// to remove when the [`Self::hard_limit`] is reached; in order to
|
||||
/// to remove when the `hard_limit` is reached; in order to
|
||||
/// allow the new login to continue.
|
||||
///
|
||||
/// Disabled by default
|
||||
@@ -187,7 +200,7 @@ pub struct SessionLimitConfig {
|
||||
/// be recovered if you have another verified active device or have a
|
||||
/// recovery key setup.
|
||||
///
|
||||
/// When using [`dangerous_hard_limit_eviction`], the [`hard_limit`] must be
|
||||
/// When using `dangerous_hard_limit_eviction`, the `hard_limit` must be
|
||||
/// at least 2 to avoid catastrophically losing encrypted history and
|
||||
/// digital identity in pathological cases. Keep in mind this is a bare
|
||||
/// minimum restriction and you can still run into trouble.
|
||||
@@ -201,13 +214,10 @@ pub struct SessionLimitConfig {
|
||||
///
|
||||
/// Removing devices is a non-trivial task for some homeservers to tackle
|
||||
/// and can cause lots of device list changes, `/sync`, federation, and
|
||||
/// replication traffic. Consider using [`max_session_threshold`] to
|
||||
/// replication traffic. Consider using `max_session_threshold` to
|
||||
/// limit the size of accounts that are acted upon.
|
||||
///
|
||||
/// [`hard_limit`]: Self::hard_limit
|
||||
/// [`dangerous_hard_limit_eviction`]: Self::dangerous_hard_limit_eviction
|
||||
/// [`max_session_threshold`]: Self::max_session_threshold
|
||||
#[serde(default = "default_false")]
|
||||
#[schemars(example = &false)]
|
||||
pub dangerous_hard_limit_eviction: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -82,34 +83,34 @@ impl UnixOrTcp {
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum BindConfig {
|
||||
/// Listen on the specified host and port
|
||||
/// Listen on the given host and port combination
|
||||
Listen {
|
||||
/// Host on which to listen.
|
||||
///
|
||||
/// Defaults to listening on all addresses
|
||||
/// Host on which to listen, defaults to all addresses
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"localhost")]
|
||||
host: Option<String>,
|
||||
|
||||
/// Port on which to listen.
|
||||
/// Port on which to listen
|
||||
#[schemars(example = &8081u16)]
|
||||
port: u16,
|
||||
},
|
||||
|
||||
/// Listen on the specified address
|
||||
/// Listen on the given address
|
||||
Address {
|
||||
/// Host and port on which to listen
|
||||
#[schemars(
|
||||
example = &"[::1]:8080",
|
||||
example = &"[::]:8080",
|
||||
example = &"[::1]:8080",
|
||||
example = &"127.0.0.1:8080",
|
||||
example = &"0.0.0.0:8080",
|
||||
)]
|
||||
address: String,
|
||||
},
|
||||
|
||||
/// Listen on a UNIX domain socket
|
||||
/// Listen on the given UNIX socket
|
||||
Unix {
|
||||
/// Path to the socket
|
||||
#[schemars(with = "String")]
|
||||
#[schemars(with = "String", example = &"/tmp/mas.sock")]
|
||||
socket: Utf8PathBuf,
|
||||
|
||||
/// Permissions to use for the socket. Defaults to the process's umask.
|
||||
@@ -118,21 +119,24 @@ pub enum BindConfig {
|
||||
mode: Option<String>,
|
||||
},
|
||||
|
||||
/// Accept connections on file descriptors passed by the parent process.
|
||||
/// Grab an already open file descriptor given by the parent process.
|
||||
///
|
||||
/// This is useful for grabbing sockets passed by systemd.
|
||||
/// This is useful when using systemd socket activation.
|
||||
///
|
||||
/// The file descriptor index is offset by 3, to account for the standard
|
||||
/// input, output and error streams, so a value of `0` grabs the file
|
||||
/// descriptor `3`.
|
||||
///
|
||||
/// See <https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html>
|
||||
FileDescriptor {
|
||||
/// Index of the file descriptor. Note that this is offseted by 3
|
||||
/// because of the standard input/output sockets, so setting
|
||||
/// here a value of `0` will grab the file descriptor `3`
|
||||
/// Index of the file descriptor to grab
|
||||
#[serde(default)]
|
||||
#[schemars(example = &1usize)]
|
||||
fd: usize,
|
||||
|
||||
/// Whether the socket is a TCP socket or a UNIX domain socket. Defaults
|
||||
/// to TCP.
|
||||
/// Kind of socket that was passed, defaults to tcp
|
||||
#[serde(default = "UnixOrTcp::tcp")]
|
||||
#[schemars(example = &UnixOrTcp::Tcp)]
|
||||
kind: UnixOrTcp,
|
||||
},
|
||||
}
|
||||
@@ -140,45 +144,36 @@ pub enum BindConfig {
|
||||
/// Configuration related to TLS on a listener
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
pub struct TlsConfig {
|
||||
/// PEM-encoded X509 certificate chain
|
||||
///
|
||||
/// Exactly one of `certificate` or `certificate_file` must be set.
|
||||
/// Inline PEM-encoded X509 certificate chain (alternative to
|
||||
/// `certificate_file`)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"<inline PEM>", extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub certificate: Option<String>,
|
||||
|
||||
/// File containing the PEM-encoded X509 certificate chain
|
||||
///
|
||||
/// Exactly one of `certificate` or `certificate_file` must be set.
|
||||
/// Path to a file containing the PEM-encoded X509 certificate chain
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/cert.pem")]
|
||||
pub certificate_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// PEM-encoded private key
|
||||
///
|
||||
/// Exactly one of `key` or `key_file` must be set.
|
||||
/// Inline PEM-encoded private key (alternative to `key_file`)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"<inline PEM>", extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub key: Option<String>,
|
||||
|
||||
/// File containing a PEM or DER-encoded private key
|
||||
///
|
||||
/// Exactly one of `key` or `key_file` must be set.
|
||||
/// Path to a file containing a PEM or DER-encoded private key
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/key.pem")]
|
||||
pub key_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// Password used to decode the private key
|
||||
///
|
||||
/// One of `password` or `password_file` must be set if the key is
|
||||
/// encrypted.
|
||||
/// Inline password used to decrypt the private key, if it is encrypted
|
||||
/// (alternative to `password_file`)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"<password to decrypt the key>", extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub password: Option<String>,
|
||||
|
||||
/// Password file used to decode the private key
|
||||
///
|
||||
/// One of `password` or `password_file` must be set if the key is
|
||||
/// encrypted.
|
||||
/// Path to a file containing the password used to decrypt the private key
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/password.txt", extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub password_file: Option<Utf8PathBuf>,
|
||||
}
|
||||
|
||||
@@ -258,51 +253,55 @@ impl TlsConfig {
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
#[serde(tag = "name", rename_all = "lowercase")]
|
||||
pub enum Resource {
|
||||
/// Healthcheck endpoint (/health)
|
||||
/// Serves the health check endpoint on `/health`
|
||||
Health,
|
||||
|
||||
/// Prometheus metrics endpoint (/metrics)
|
||||
/// Serves a Prometheus-compatible metrics endpoint on `/metrics`, if the
|
||||
/// Prometheus exporter is enabled in `telemetry.metrics.exporter`
|
||||
Prometheus,
|
||||
|
||||
/// OIDC discovery endpoints
|
||||
/// Serves the `.well-known/openid-configuration` document
|
||||
Discovery,
|
||||
|
||||
/// Pages destined to be viewed by humans
|
||||
/// Serves the human-facing pages, such as the login page
|
||||
Human,
|
||||
|
||||
/// GraphQL endpoint
|
||||
/// Serves the GraphQL API used by the frontend, and optionally the GraphQL
|
||||
/// playground
|
||||
GraphQL {
|
||||
/// Enabled the GraphQL playground
|
||||
/// Enable the GraphQL playground
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
#[schemars(example = &true)]
|
||||
playground: bool,
|
||||
|
||||
/// Allow access for OAuth 2.0 clients (undocumented)
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
undocumented_oauth2_access: bool,
|
||||
},
|
||||
|
||||
/// OAuth-related APIs
|
||||
/// Serves the OAuth 2.0/OIDC endpoints
|
||||
OAuth,
|
||||
|
||||
/// Matrix compatibility API
|
||||
/// Serves the Matrix C-S API compatibility endpoints
|
||||
Compat,
|
||||
|
||||
/// Static files
|
||||
/// Serves the given folder on the `/assets/` path
|
||||
Assets {
|
||||
/// Path to the directory to serve.
|
||||
/// Path to the directory to serve
|
||||
#[serde(
|
||||
default = "http_listener_assets_path_default",
|
||||
skip_serializing_if = "is_default_http_listener_assets_path"
|
||||
)]
|
||||
#[schemars(with = "String")]
|
||||
#[schemars(with = "String", example = &"./share/assets/")]
|
||||
path: Utf8PathBuf,
|
||||
},
|
||||
|
||||
/// Admin API, served at `/api/admin/v1`
|
||||
/// Serves the admin API on the `/api/admin/v1/` path. Disabled by default
|
||||
AdminApi,
|
||||
|
||||
/// Mount a "/connection-info" handler which helps debugging informations on
|
||||
/// the upstream connection
|
||||
/// Mounts a `/connection-info` handler which shows debugging information
|
||||
/// about the upstream connection
|
||||
#[serde(rename = "connection-info")]
|
||||
ConnectionInfo,
|
||||
}
|
||||
@@ -310,23 +309,25 @@ pub enum Resource {
|
||||
/// Configuration of a listener
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
pub struct ListenerConfig {
|
||||
/// A unique name for this listener which will be shown in traces and in
|
||||
/// metrics labels
|
||||
/// The name of the listener, used in logs and metrics
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"web")]
|
||||
pub name: Option<String>,
|
||||
|
||||
/// List of resources to mount
|
||||
/// List of resources to serve
|
||||
pub resources: Vec<Resource>,
|
||||
|
||||
/// HTTP prefix to mount the resources on
|
||||
/// Optional URL prefix to mount all the resources of this listener under
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"/auth", extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub prefix: Option<String>,
|
||||
|
||||
/// List of sockets to bind
|
||||
/// List of addresses and ports to listen to
|
||||
pub binds: Vec<BindConfig>,
|
||||
|
||||
/// Accept `HAProxy`'s Proxy Protocol V1
|
||||
/// Whether to enable the PROXY protocol on the listener
|
||||
#[serde(default)]
|
||||
#[schemars(example = &false)]
|
||||
pub proxy_protocol: bool,
|
||||
|
||||
/// If set, makes the listener use TLS with the provided certificate and key
|
||||
@@ -334,24 +335,51 @@ pub struct ListenerConfig {
|
||||
pub tls: Option<TlsConfig>,
|
||||
}
|
||||
|
||||
/// Configuration related to the web server
|
||||
/// Controls the web server.
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct HttpConfig {
|
||||
/// List of listeners to run
|
||||
/// Each listener can serve multiple resources, and listen on multiple TCP
|
||||
/// ports or UNIX sockets.
|
||||
///
|
||||
/// <!-- more -->
|
||||
///
|
||||
/// The following additional resources are available, although it is
|
||||
/// recommended to serve them on a separate listener, not exposed to the
|
||||
/// public internet:
|
||||
///
|
||||
/// - `name: prometheus`: serves a Prometheus-compatible metrics endpoint on
|
||||
/// `/metrics`, if the Prometheus exporter is enabled in
|
||||
/// `telemetry.metrics.exporter`.
|
||||
/// - `name: health`: serves the health check endpoint on `/health`.
|
||||
#[serde(default)]
|
||||
pub listeners: Vec<ListenerConfig>,
|
||||
|
||||
/// List of trusted reverse proxies that can set the `X-Forwarded-For`
|
||||
/// header
|
||||
/// List of trusted reverse proxies that are allowed to set the
|
||||
/// `X-Forwarded-For` header.
|
||||
///
|
||||
/// Defaults to the usual private IP ranges:
|
||||
/// 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 127.0.0.0/8,
|
||||
/// fd00::/8 and ::1/128
|
||||
#[expect(
|
||||
clippy::doc_markdown,
|
||||
reason = "the IPv6 ranges are shown verbatim in the rendered config reference"
|
||||
)]
|
||||
#[serde(default = "default_trusted_proxies")]
|
||||
#[schemars(with = "Vec<String>", inner(ip))]
|
||||
#[schemars(
|
||||
with = "Vec<String>",
|
||||
inner(ip),
|
||||
example = &["192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8", "127.0.0.0/8", "fd00::/8", "::1/128"],
|
||||
extend("x-doc" = serde_json::json!({ "commented": true }))
|
||||
)]
|
||||
pub trusted_proxies: Vec<IpNetwork>,
|
||||
|
||||
/// Public URL base from where the authentication service is reachable
|
||||
/// Public URL base used when building absolute public URLs
|
||||
#[schemars(example = &"https://auth.example.com/")]
|
||||
pub public_base: Url,
|
||||
|
||||
/// OIDC issuer URL. Defaults to `public_base` if not set.
|
||||
/// OIDC issuer advertised by the service. Defaults to `public_base`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"https://example.com/")]
|
||||
pub issuer: Option<Url>,
|
||||
}
|
||||
|
||||
|
||||
@@ -30,21 +30,19 @@ fn default_endpoint() -> Url {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HomeserverKind {
|
||||
/// Homeserver is Synapse, version 1.135.0 or newer
|
||||
/// Synapse, version 1.135.0 or newer
|
||||
#[default]
|
||||
Synapse,
|
||||
|
||||
/// Homeserver is Synapse, version 1.135.0 or newer, in read-only mode
|
||||
///
|
||||
/// This is meant for testing rolling out Matrix Authentication Service with
|
||||
/// no risk of writing data to the homeserver.
|
||||
/// same as `synapse`, but in read-only mode. This is meant for testing
|
||||
/// rolling out MAS with no risk of writing data to the homeserver.
|
||||
SynapseReadOnly,
|
||||
|
||||
/// Homeserver is Synapse, using the legacy API
|
||||
SynapseLegacy,
|
||||
|
||||
/// Homeserver is Synapse, with the modern API available (>= 1.135.0)
|
||||
/// Synapse with the modern admin API available (>= 1.135.0)
|
||||
SynapseModern,
|
||||
|
||||
/// Synapse using the legacy admin API
|
||||
SynapseLegacy,
|
||||
}
|
||||
|
||||
/// Shared secret between MAS and the homeserver.
|
||||
@@ -60,9 +58,16 @@ pub enum Secret {
|
||||
/// Secret fields as serialized in JSON.
|
||||
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
|
||||
struct SecretRaw {
|
||||
#[schemars(with = "Option<String>")]
|
||||
/// Shared secret used to authenticate the service to the homeserver.
|
||||
/// This must be of high entropy, because leaking this secret would allow
|
||||
/// anyone to perform admin actions on the homeserver.
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/secret/file")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
secret_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// Alternatively, the shared secret can be passed inline.
|
||||
#[schemars(example = &"SomeRandomSecret")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
secret: Option<String>,
|
||||
}
|
||||
@@ -95,16 +100,19 @@ impl From<Secret> for SecretRaw {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration related to the Matrix homeserver
|
||||
/// Settings related to the connection to the Matrix homeserver
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MatrixConfig {
|
||||
/// The kind of homeserver it is.
|
||||
/// The kind of homeserver it is. Defaults to `synapse`.
|
||||
#[serde(default)]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub kind: HomeserverKind,
|
||||
|
||||
/// The server name of the homeserver.
|
||||
/// The homeserver name, as per the `server_name` in the Synapse
|
||||
/// configuration file.
|
||||
#[serde(default = "default_homeserver")]
|
||||
#[schemars(example = &"example.com")]
|
||||
pub homeserver: String,
|
||||
|
||||
/// Shared secret to use for calls to the admin API
|
||||
@@ -113,8 +121,9 @@ pub struct MatrixConfig {
|
||||
#[serde(flatten)]
|
||||
pub secret: Secret,
|
||||
|
||||
/// The base URL of the homeserver's client API
|
||||
/// URL to which the homeserver is accessible from the service.
|
||||
#[serde(default = "default_endpoint")]
|
||||
#[schemars(example = &"http://localhost:8008")]
|
||||
pub endpoint: Url,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -68,7 +69,15 @@ use crate::util::ConfigurationSection;
|
||||
/// Application configuration root
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct RootConfig {
|
||||
/// List of OAuth 2.0/OIDC clients config
|
||||
/// List of OAuth 2.0/OIDC clients and their keys/secrets. Each `client_id`
|
||||
/// must be a [ULID](https://github.com/ulid/spec).
|
||||
///
|
||||
/// <!-- more -->
|
||||
///
|
||||
/// **Note:** any additions or modifications in this list are synced with
|
||||
/// the database on server startup. Removed entries are only removed with
|
||||
/// the [`config sync
|
||||
/// --prune`](./cli/config.md#config-sync---prune---dry-run) command.
|
||||
#[serde(default, skip_serializing_if = "ClientsConfig::is_default")]
|
||||
pub clients: ClientsConfig,
|
||||
|
||||
@@ -338,12 +347,16 @@ pub struct ClientSecretRaw {
|
||||
/// Path to the file containing the client secret. The client secret is used
|
||||
/// by the `client_secret_basic`, `client_secret_post` and
|
||||
/// `client_secret_jwt` authentication methods.
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"secret")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
client_secret_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// Alternative to `client_secret_file`: Reads the client secret directly
|
||||
/// from the config.
|
||||
#[schemars(
|
||||
example = &"f4f6bb68a0269264877e9cb23b1856ab",
|
||||
extend("x-doc" = {"commented": true})
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
client_secret: Option<String>,
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ const fn is_default_false(value: &bool) -> bool {
|
||||
*value == default_false()
|
||||
}
|
||||
|
||||
/// Configuration section for OAuth 2.0 protocol options
|
||||
/// Configuration section for OAuth 2.0 protocol options.
|
||||
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
|
||||
pub struct OAuthConfig {
|
||||
/// Whether the Device Authorization Grant (RFC 8628) is enabled. Defaults
|
||||
@@ -39,6 +39,7 @@ pub struct OAuthConfig {
|
||||
/// `urn:ietf:params:oauth:grant-type:device_code` grant type will be
|
||||
/// rejected.
|
||||
#[serde(default = "default_true", skip_serializing_if = "is_default_true")]
|
||||
#[schemars(example = &true)]
|
||||
pub device_code_grant_enabled: bool,
|
||||
|
||||
/// Whether the device authorization endpoint advertises a
|
||||
@@ -50,6 +51,7 @@ pub struct OAuthConfig {
|
||||
/// `code` query parameter, forcing users to type their user code
|
||||
/// manually.
|
||||
#[serde(default = "default_false", skip_serializing_if = "is_default_false")]
|
||||
#[schemars(example = &false)]
|
||||
pub device_code_user_code_auto_fill_enabled: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -32,29 +33,28 @@ fn default_minimum_complexity() -> u8 {
|
||||
3
|
||||
}
|
||||
|
||||
/// User password hashing config
|
||||
/// Settings related to the local password database
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct PasswordsConfig {
|
||||
/// Whether password-based authentication is enabled
|
||||
/// Whether to enable the password database.
|
||||
///
|
||||
/// If disabled, users will only be able to log in using upstream OIDC
|
||||
/// providers
|
||||
#[serde(default = "default_enabled")]
|
||||
pub enabled: bool,
|
||||
|
||||
/// The hashing schemes to use for hashing and validating passwords
|
||||
/// List of password hashing schemes being used
|
||||
///
|
||||
/// The hashing scheme with the highest version number will be used for
|
||||
/// hashing new passwords.
|
||||
/// /!\ Only change this if you know what you're doing
|
||||
#[serde(default = "default_schemes")]
|
||||
pub schemes: Vec<HashingScheme>,
|
||||
|
||||
/// Score between 0 and 4 determining the minimum allowed password
|
||||
/// complexity. Scores are based on the ESTIMATED number of guesses
|
||||
/// needed to guess the password.
|
||||
/// Minimum complexity required for passwords, estimated by the zxcvbn
|
||||
/// algorithm
|
||||
///
|
||||
/// - 0: less than 10^2 (100)
|
||||
/// - 1: less than 10^4 (10'000)
|
||||
/// - 2: less than 10^6 (1'000'000)
|
||||
/// - 3: less than 10^8 (100'000'000)
|
||||
/// - 4: any more than that
|
||||
/// Must be between 0 and 4, default is 3
|
||||
///
|
||||
/// See <https://github.com/dropbox/zxcvbn#usage> for more information
|
||||
#[serde(default = "default_minimum_complexity")]
|
||||
minimum_complexity: u8,
|
||||
}
|
||||
@@ -178,34 +178,47 @@ const fn is_default_false(value: &bool) -> bool {
|
||||
/// Parameters for a password hashing scheme
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct HashingScheme {
|
||||
/// The version of the hashing scheme. They must be unique, and the highest
|
||||
/// version will be used for hashing new passwords.
|
||||
/// The version of the hashing scheme. Must be unique; the highest version
|
||||
/// is used for hashing new passwords, the others are kept to verify
|
||||
/// existing passwords.
|
||||
#[schemars(example = &1u16)]
|
||||
pub version: u16,
|
||||
|
||||
/// The hashing algorithm to use
|
||||
/// The hashing algorithm to use.
|
||||
#[schemars(example = &Algorithm::Argon2id)]
|
||||
pub algorithm: Algorithm,
|
||||
|
||||
/// Whether to apply Unicode normalization to the password before hashing
|
||||
/// Whether to apply Unicode normalization to the password before hashing.
|
||||
///
|
||||
/// Defaults to `false`, and generally recommended to stay false. This is
|
||||
/// although recommended when importing password hashs from Synapse, as it
|
||||
/// applies an NFKC normalization to the password before hashing it.
|
||||
/// recommended when importing password hashes from Synapse, which applies
|
||||
/// an NFKC normalization to the password before hashing it.
|
||||
#[serde(default, skip_serializing_if = "is_default_false")]
|
||||
#[schemars(example = &false)]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub unicode_normalization: bool,
|
||||
|
||||
/// Cost for the bcrypt algorithm
|
||||
/// Cost for the bcrypt algorithm. Defaults to `12`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(default = "default_bcrypt_cost")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub cost: Option<u32>,
|
||||
|
||||
/// An optional secret to use when hashing passwords. This makes it harder
|
||||
/// to brute-force the passwords in case of a database leak.
|
||||
/// An optional secret ("pepper") to use when hashing passwords. This makes
|
||||
/// it harder to brute-force the passwords in case of a database leak.
|
||||
///
|
||||
/// This must not be specified if `secret_file` is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"<secret>")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub secret: Option<String>,
|
||||
|
||||
/// Same as `secret`, but read from a file.
|
||||
///
|
||||
/// This must not be specified if `secret` is specified.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/secret")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "commented": true })))]
|
||||
pub secret_file: Option<Utf8PathBuf>,
|
||||
}
|
||||
|
||||
@@ -218,13 +231,13 @@ fn default_bcrypt_cost() -> Option<u32> {
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Algorithm {
|
||||
/// bcrypt
|
||||
/// The bcrypt password hashing algorithm.
|
||||
Bcrypt,
|
||||
|
||||
/// argon2id
|
||||
/// The Argon2id password hashing algorithm. This is the default.
|
||||
#[default]
|
||||
Argon2id,
|
||||
|
||||
/// PBKDF2
|
||||
/// The PBKDF2 password hashing algorithm.
|
||||
Pbkdf2,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -86,16 +87,21 @@ fn is_default_data(value: &serde_json::Value) -> bool {
|
||||
*value == default_data()
|
||||
}
|
||||
|
||||
/// Application secrets
|
||||
/// Policy settings
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct PolicyConfig {
|
||||
/// Path to the WASM module
|
||||
///
|
||||
/// The default value depends on how MAS was built:
|
||||
/// - Docker distribution: `/usr/local/share/mas-cli/policy.wasm`
|
||||
/// - pre-built binaries: `./share/policy.wasm`
|
||||
/// - locally-built binaries: `./policies/policy.wasm`
|
||||
#[serde(
|
||||
default = "default_policy_path",
|
||||
skip_serializing_if = "is_default_policy_path"
|
||||
)]
|
||||
#[schemars(with = "String")]
|
||||
#[schemars(with = "String", example = &"./policies/policy.wasm")]
|
||||
pub wasm_module: Utf8PathBuf,
|
||||
|
||||
/// Entrypoint to use when evaluating client registrations
|
||||
@@ -103,6 +109,7 @@ pub struct PolicyConfig {
|
||||
default = "default_client_registration_entrypoint",
|
||||
skip_serializing_if = "is_default_client_registration_entrypoint"
|
||||
)]
|
||||
#[schemars(example = &"client_registration/violation")]
|
||||
pub client_registration_entrypoint: String,
|
||||
|
||||
/// Entrypoint to use when evaluating user registrations
|
||||
@@ -110,6 +117,7 @@ pub struct PolicyConfig {
|
||||
default = "default_register_entrypoint",
|
||||
skip_serializing_if = "is_default_register_entrypoint"
|
||||
)]
|
||||
#[schemars(example = &"register/violation")]
|
||||
pub register_entrypoint: String,
|
||||
|
||||
/// Entrypoint to use when evaluating authorization grants
|
||||
@@ -117,20 +125,15 @@ pub struct PolicyConfig {
|
||||
default = "default_authorization_grant_entrypoint",
|
||||
skip_serializing_if = "is_default_authorization_grant_entrypoint"
|
||||
)]
|
||||
#[schemars(example = &"authorization_grant/violation")]
|
||||
pub authorization_grant_entrypoint: String,
|
||||
|
||||
/// Entrypoint to use when evaluating compatibility logins
|
||||
#[serde(
|
||||
default = "default_compat_login_entrypoint",
|
||||
skip_serializing_if = "is_default_compat_login_entrypoint"
|
||||
)]
|
||||
pub compat_login_entrypoint: String,
|
||||
|
||||
/// Entrypoint to use when changing password
|
||||
#[serde(
|
||||
default = "default_password_entrypoint",
|
||||
skip_serializing_if = "is_default_password_entrypoint"
|
||||
)]
|
||||
#[schemars(example = &"password/violation")]
|
||||
pub password_entrypoint: String,
|
||||
|
||||
/// Entrypoint to use when adding an email address
|
||||
@@ -138,10 +141,113 @@ pub struct PolicyConfig {
|
||||
default = "default_email_entrypoint",
|
||||
skip_serializing_if = "is_default_email_entrypoint"
|
||||
)]
|
||||
#[schemars(example = &"email/violation")]
|
||||
pub email_entrypoint: String,
|
||||
|
||||
/// Entrypoint to use when evaluating compatibility logins
|
||||
#[serde(
|
||||
default = "default_compat_login_entrypoint",
|
||||
skip_serializing_if = "is_default_compat_login_entrypoint"
|
||||
)]
|
||||
#[schemars(example = &"compat_login/violation")]
|
||||
pub compat_login_entrypoint: String,
|
||||
|
||||
/// Arbitrary data to pass to the policy
|
||||
#[serde(default = "default_data", skip_serializing_if = "is_default_data")]
|
||||
#[schemars(extend("x-doc" = {"yaml": r#"
|
||||
# This data is being passed to the policy
|
||||
data:
|
||||
# Users which are allowed to ask for admin access. If possible, use the
|
||||
# can_request_admin flag on users instead.
|
||||
admin_users:
|
||||
- person1
|
||||
- person2
|
||||
|
||||
# Client IDs which are allowed to ask for admin access with a
|
||||
# client_credentials grant
|
||||
admin_clients:
|
||||
- 01H8PKNWKKRPCBW4YGH1RWV279
|
||||
- 01HWQCPA5KF10FNCETY9402WGF
|
||||
|
||||
# Dynamic Client Registration
|
||||
client_registration:
|
||||
# don't require URIs to be on the same host. default: false
|
||||
allow_host_mismatch: false
|
||||
# allow non-SSL and localhost URIs. default: false
|
||||
allow_insecure_uris: false
|
||||
# don't require clients to provide a client_uri. default: false
|
||||
allow_missing_client_uri: false
|
||||
|
||||
# Restrictions on user registration
|
||||
registration:
|
||||
# If specified, the username (localpart) *must* match one of the allowed
|
||||
# usernames. If unspecified, all usernames are allowed.
|
||||
allowed_usernames:
|
||||
# Exact usernames that are allowed
|
||||
literals: ["alice", "bob"]
|
||||
# Substrings that match allowed usernames
|
||||
substrings: ["user"]
|
||||
# Regular expressions that match allowed usernames
|
||||
regexes: ["^[a-z]+$"]
|
||||
# Prefixes that match allowed usernames
|
||||
prefixes: ["user-"]
|
||||
# Suffixes that match allowed usernames
|
||||
suffixes: ["-corp"]
|
||||
# If specified, the username (localpart) *must not* match one of the
|
||||
# banned usernames. If unspecified, all usernames are allowed.
|
||||
banned_usernames:
|
||||
# Exact usernames that are banned
|
||||
literals: ["admin", "root"]
|
||||
# Substrings that match banned usernames
|
||||
substrings: ["admin", "root"]
|
||||
# Regular expressions that match banned usernames
|
||||
regexes: ["^admin$", "^root$"]
|
||||
# Prefixes that match banned usernames
|
||||
prefixes: ["admin-", "root-"]
|
||||
# Suffixes that match banned usernames
|
||||
suffixes: ["-admin", "-root"]
|
||||
|
||||
# Restrict what email addresses can be added to a user
|
||||
emails:
|
||||
# If specified, the email address *must* match one of the allowed addresses.
|
||||
# If unspecified, all email addresses are allowed.
|
||||
allowed_addresses:
|
||||
# Exact emails that are allowed
|
||||
literals: ["alice@example.com", "bob@example.com"]
|
||||
# Regular expressions that match allowed emails
|
||||
regexes: ["@example\\.com$"]
|
||||
# Suffixes that match allowed emails
|
||||
suffixes: ["@example.com"]
|
||||
|
||||
# If specified, the email address *must not* match one of the banned addresses.
|
||||
# If unspecified, all email addresses are allowed.
|
||||
banned_addresses:
|
||||
# Exact emails that are banned
|
||||
literals: ["alice@evil.corp", "bob@evil.corp"]
|
||||
# Emails that contains those substrings are banned
|
||||
substrings: ["evil"]
|
||||
# Regular expressions that match banned emails
|
||||
regexes: ["@evil\\.corp$"]
|
||||
# Suffixes that match banned emails
|
||||
suffixes: ["@evil.corp"]
|
||||
# Prefixes that match banned emails
|
||||
prefixes: ["alice@"]
|
||||
|
||||
requester:
|
||||
# List of IP addresses and CIDRs that are not allowed to register
|
||||
banned_ips:
|
||||
- 192.168.0.1
|
||||
- 192.168.1.0/24
|
||||
- fe80::/64
|
||||
|
||||
# User agent patterns that are not allowed to register
|
||||
banned_user_agents:
|
||||
literals: ["Pretend this is Real;"]
|
||||
substrings: ["Chrome"]
|
||||
regexes: ["Chrome 1.*;"]
|
||||
prefixes: ["Mozilla/"]
|
||||
suffixes: ["Safari/605.1.15"]
|
||||
"#}))]
|
||||
pub data: serde_json::Value,
|
||||
}
|
||||
|
||||
@@ -152,9 +258,9 @@ impl Default for PolicyConfig {
|
||||
client_registration_entrypoint: default_client_registration_entrypoint(),
|
||||
register_entrypoint: default_register_entrypoint(),
|
||||
authorization_grant_entrypoint: default_authorization_grant_entrypoint(),
|
||||
compat_login_entrypoint: default_compat_login_entrypoint(),
|
||||
password_entrypoint: default_password_entrypoint(),
|
||||
email_entrypoint: default_email_entrypoint(),
|
||||
compat_login_entrypoint: default_compat_login_entrypoint(),
|
||||
data: default_data(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -12,23 +13,37 @@ use serde::{Deserialize, Serialize, de::Error as _};
|
||||
|
||||
use crate::ConfigurationSection;
|
||||
|
||||
/// Configuration related to sending emails
|
||||
/// Settings for limiting the rate of user actions to prevent abuse.
|
||||
///
|
||||
/// Each rate limiter consists of two options:
|
||||
/// - `burst`: a base amount of how many actions are allowed in one go.
|
||||
/// - `per_second`: how many units of the allowance replenish per second.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
pub struct RateLimitingConfig {
|
||||
/// Account Recovery-specific rate limits
|
||||
/// Limits how many account recovery attempts are allowed.
|
||||
/// These limits can protect against e-mail spam.
|
||||
///
|
||||
/// Note: these limit also apply to recovery e-mail re-sends.
|
||||
#[serde(default)]
|
||||
pub account_recovery: AccountRecoveryRateLimitingConfig,
|
||||
|
||||
/// Login-specific rate limits
|
||||
/// Limits how many login attempts are allowed.
|
||||
///
|
||||
/// Note: these limit also applies to password checks when a user attempts
|
||||
/// to change their own password.
|
||||
#[serde(default)]
|
||||
pub login: LoginRateLimitingConfig,
|
||||
|
||||
/// Controls how many registrations attempts are permitted
|
||||
/// based on source address.
|
||||
/// Limits how many registrations attempts are allowed,
|
||||
/// based on source IP address.
|
||||
/// This limit can protect against e-mail spam and against people
|
||||
/// registering too many accounts.
|
||||
#[serde(default = "default_registration")]
|
||||
pub registration: RateLimiterConfiguration,
|
||||
|
||||
/// Email authentication-specific rate limits
|
||||
/// Limits how many e-mail authentication attempts are allowed.
|
||||
/// These limits can protect against e-mail spam and against brute-forcing
|
||||
/// the verification code.
|
||||
#[serde(default)]
|
||||
pub email_authentication: EmailauthenticationRateLimitingConfig,
|
||||
}
|
||||
|
||||
@@ -199,14 +199,17 @@ pub enum Encryption {
|
||||
struct EncryptionRaw {
|
||||
/// File containing the encryption key for secure cookies.
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
encryption_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// Encryption key for secure cookies.
|
||||
/// Encryption secret (used for encrypting cookies and database fields)
|
||||
///
|
||||
/// This must be a 32-byte long hex-encoded key
|
||||
#[schemars(
|
||||
with = "Option<String>",
|
||||
regex(pattern = r"[0-9a-fA-F]{64}"),
|
||||
example = &"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff"
|
||||
example = &"c7e42fb8baba8f228b2e169fdf4c8216dffd5d33ad18bafd8b928c09ca46c718"
|
||||
)]
|
||||
#[serde_as(as = "Option<serde_with::hex::Hex>")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -258,7 +261,82 @@ async fn key_configs_from_path(path: &Utf8PathBuf) -> anyhow::Result<Vec<KeyConf
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Application secrets
|
||||
/// Signing and encryption secrets
|
||||
///
|
||||
/// <!-- more -->
|
||||
///
|
||||
/// ### `secrets.encryption{_file}`
|
||||
///
|
||||
/// The encryption secret used for encrypting cookies and database fields. It
|
||||
/// takes the form of a 32-bytes-long hex-encoded string. To provide the
|
||||
/// encryption secret via file, set `secrets.encryption_file` to the file path;
|
||||
/// alternatively use `secrets.encryption` for declaring the secret inline. The
|
||||
/// options `secrets.encryption_file` and `secrets.encryption` are mutually
|
||||
/// exclusive.
|
||||
///
|
||||
/// If given via file, the encryption secret is only read at application
|
||||
/// startup. The secret is not updated when the content of the file changes.
|
||||
///
|
||||
/// > ⚠️ **Warning** – Do not change the encryption secret after the initial
|
||||
/// > start! Changing the encryption secret afterwards will lead to a loss of
|
||||
/// > all encrypted information in the database.
|
||||
///
|
||||
/// ### Signing Keys
|
||||
///
|
||||
/// The service can use a number of key types for signing.
|
||||
/// The following key types are supported:
|
||||
///
|
||||
/// - RSA
|
||||
/// - ECDSA with the P-256 (`prime256v1`) curve
|
||||
/// - ECDSA with the P-384 (`secp384r1`) curve
|
||||
/// - ECDSA with the K-256 (`secp256k1`) curve
|
||||
///
|
||||
/// The following key formats are supported:
|
||||
///
|
||||
/// - PKCS#1 PEM or DER-encoded RSA private key
|
||||
/// - PKCS#8 PEM or DER-encoded RSA or ECDSA private key, encrypted or not
|
||||
/// - SEC1 PEM or DER-encoded ECDSA private key
|
||||
///
|
||||
/// The signing keys are used for:
|
||||
/// - signing ID Tokens (as returned in the [Token Endpoint] at
|
||||
/// `/oauth2/token`);
|
||||
/// - signing the response of the [UserInfo Endpoint] at `/oauth2/userinfo` if
|
||||
/// the client requests a signed response;
|
||||
/// - (niche) signing a JWT for authenticating to an upstream OAuth provider
|
||||
/// when the `private_key_jwt` client auth method is configured.
|
||||
///
|
||||
/// At a minimum, an RSA key must be configured in order to be compliant with
|
||||
/// the [OpenID Connect Core specification][oidc-core-rs256] which specifies the
|
||||
/// RS256 algorithm as mandatory to implement by servers for interoperability
|
||||
/// reasons.
|
||||
///
|
||||
/// The keys can be given as a directory path via `secrets.keys_dir`
|
||||
/// or, alternatively, as an inline configuration list via `secrets.keys`.
|
||||
///
|
||||
/// [Token Endpoint]: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
||||
/// [UserInfo Endpoint]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
|
||||
/// [oidc-core-rs256]: https://openid.net/specs/openid-connect-core-1_0.html#ServerMTI
|
||||
///
|
||||
/// #### `secrets.keys_dir`
|
||||
///
|
||||
/// Path to the directory containing MAS signing key files.
|
||||
/// Only keys that don’t require a password are supported.
|
||||
///
|
||||
/// #### `secrets.keys`
|
||||
///
|
||||
/// Each entry in the list corresponds to one signing key used by MAS.
|
||||
/// The key can either be specified inline (with the `key` property),
|
||||
/// or loaded from a file (with the `key_file` property).
|
||||
///
|
||||
/// A [JWK Key ID] is automatically derived from each key.
|
||||
/// To override this default, set `kid` to a custom value.
|
||||
/// The `kid` can be any case-sensitive string value as long as it is unique to
|
||||
/// this list; a key’s `kid` value must be stable across restarts.
|
||||
///
|
||||
/// For PKCS#8 encoded keys, the `password` or `password_file` properties can be
|
||||
/// used to decrypt the key.
|
||||
///
|
||||
/// [JWK Key ID]: <https://datatracker.ietf.org/doc/html/rfc7517#section-4.5>
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct SecretsConfig {
|
||||
@@ -269,11 +347,21 @@ pub struct SecretsConfig {
|
||||
encryption: Encryption,
|
||||
|
||||
/// List of private keys to use for signing and encrypting payloads.
|
||||
///
|
||||
/// At least one RSA key must be configured.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &serde_json::json!([
|
||||
{ "key_file": "keys/rsa_key" },
|
||||
{
|
||||
"kid": "iv1aShae",
|
||||
"key": "-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIE8yeUh111Npqu2e5wXxjC/GA5lbGe0j0KVXqZP12vqioAcGBSuBBAAK\noUQDQgAESKfUtKaLqCfhK+p3z870W59yOYvd+kjGWe+tK16SmWzZJbRCgdHakHE5\nMC6tJRnvedsYoKTrYoDv/XZIBI9zlA==\n-----END EC PRIVATE KEY-----"
|
||||
}
|
||||
]))]
|
||||
keys: Option<Vec<KeyConfig>>,
|
||||
|
||||
/// Directory of private keys to use for signing and encrypting payloads.
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(extend("x-doc" = serde_json::json!({ "skip": true })))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
keys_dir: Option<Utf8PathBuf>,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -49,24 +50,37 @@ pub enum TracingExporterKind {
|
||||
/// Configuration related to exporting traces
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct TracingConfig {
|
||||
/// Exporter to use when exporting traces
|
||||
/// List of propagators to use for extracting and injecting trace contexts
|
||||
#[serde(default)]
|
||||
pub propagators: Vec<Propagator>,
|
||||
|
||||
/// Exporter to use when exporting traces
|
||||
///
|
||||
/// Set to `otlp` to export traces to an OTLP-compatible endpoint (which
|
||||
/// also requires setting `endpoint`), or to `stdout` to print traces to
|
||||
/// the standard output. Defaults to `none`, which disables trace
|
||||
/// exporting.
|
||||
#[serde(default)]
|
||||
#[schemars(extend("x-doc" = {"yaml": r"# The default: don't export traces
|
||||
exporter: none
|
||||
|
||||
# Export traces to an OTLP-compatible endpoint
|
||||
#exporter: otlp
|
||||
#endpoint: https://localhost:4318
|
||||
|
||||
# Export traces to the standard output. Only useful for debugging
|
||||
#exporter: stdout"}))]
|
||||
pub exporter: TracingExporterKind,
|
||||
|
||||
/// OTLP exporter: OTLP over HTTP compatible endpoint
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(url, default = "otlp_endpoint_default")]
|
||||
#[schemars(url, default = "otlp_endpoint_default", extend("x-doc" = {"skip": true}))]
|
||||
pub endpoint: Option<Url>,
|
||||
|
||||
/// List of propagation formats to use for incoming and outgoing requests
|
||||
#[serde(default)]
|
||||
pub propagators: Vec<Propagator>,
|
||||
|
||||
/// Sample rate for traces
|
||||
///
|
||||
/// Defaults to `1.0` if not set.
|
||||
/// Sample rate for traces, between 0.0 and 1.0. Defaults to `1.0` if not
|
||||
/// set.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = 0.5, range(min = 0.0, max = 1.0))]
|
||||
#[schemars(example = 0.5, range(min = 0.0, max = 1.0), extend("x-doc" = {"commented": true}))]
|
||||
pub sample_rate: Option<f64>,
|
||||
}
|
||||
|
||||
@@ -103,12 +117,30 @@ pub enum MetricsExporterKind {
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MetricsConfig {
|
||||
/// Exporter to use when exporting metrics
|
||||
///
|
||||
/// Set to `otlp` to export metrics to an OTLP-compatible endpoint (which
|
||||
/// also requires setting `endpoint`), to `prometheus` to expose a
|
||||
/// Prometheus endpoint, or to `stdout` to print metrics to the standard
|
||||
/// output. Defaults to `none`, which disables metric exporting.
|
||||
#[serde(default)]
|
||||
#[schemars(extend("x-doc" = {"yaml": r"# The default: don't export metrics
|
||||
exporter: none
|
||||
|
||||
# Export metrics to an OTLP-compatible endpoint
|
||||
#exporter: otlp
|
||||
#endpoint: https://localhost:4317
|
||||
|
||||
# Export metrics by exposing a Prometheus endpoint
|
||||
# This requires mounting the `prometheus` resource to an HTTP listener
|
||||
#exporter: prometheus
|
||||
|
||||
# Export metrics to the standard output. Only useful for debugging
|
||||
#exporter: stdout"}))]
|
||||
pub exporter: MetricsExporterKind,
|
||||
|
||||
/// OTLP exporter: OTLP over HTTP compatible endpoint
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(url, default = "otlp_endpoint_default")]
|
||||
#[schemars(url, default = "otlp_endpoint_default", extend("x-doc" = {"skip": true}))]
|
||||
pub endpoint: Option<Url>,
|
||||
}
|
||||
|
||||
@@ -122,30 +154,27 @@ impl MetricsConfig {
|
||||
/// Configuration related to the Sentry integration
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct SentryConfig {
|
||||
/// Sentry DSN
|
||||
/// DSN to use for sending errors and crashes to Sentry
|
||||
#[schemars(url, example = &"https://public@host:port/1")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub dsn: Option<String>,
|
||||
|
||||
/// Environment to use when sending events to Sentry
|
||||
///
|
||||
/// Defaults to `production` if not set.
|
||||
#[schemars(example = &"production")]
|
||||
/// Environment to use when sending events to Sentry. Defaults to
|
||||
/// `production`.
|
||||
#[schemars(example = &"production", extend("x-doc" = {"commented": true}))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub environment: Option<String>,
|
||||
|
||||
/// Sample rate for event submissions
|
||||
///
|
||||
/// Defaults to `1.0` if not set.
|
||||
/// Sample rate for event submissions, between 0.0 and 1.0. Defaults to
|
||||
/// `1.0`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = 0.5, range(min = 0.0, max = 1.0))]
|
||||
#[schemars(example = 1.0, range(min = 0.0, max = 1.0), extend("x-doc" = {"commented": true}))]
|
||||
pub sample_rate: Option<f32>,
|
||||
|
||||
/// Sample rate for tracing transactions
|
||||
///
|
||||
/// Defaults to `0.0` if not set.
|
||||
/// Sample rate for tracing transactions, between 0.0 and 1.0. Defaults to
|
||||
/// `0.0`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = 0.5, range(min = 0.0, max = 1.0))]
|
||||
#[schemars(example = 0.0, range(min = 0.0, max = 1.0), extend("x-doc" = {"commented": true}))]
|
||||
pub traces_sample_rate: Option<f32>,
|
||||
}
|
||||
|
||||
@@ -156,7 +185,7 @@ impl SentryConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration related to sending monitoring data
|
||||
/// Settings related to metrics and traces
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct TelemetryConfig {
|
||||
/// Configuration related to exporting traces
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -67,28 +68,36 @@ fn is_default_translations_path(value: &Utf8PathBuf) -> bool {
|
||||
*value == default_translations_path()
|
||||
}
|
||||
|
||||
/// Configuration related to templates
|
||||
/// Allows loading custom templates
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
pub struct TemplatesConfig {
|
||||
/// Path to the folder which holds the templates
|
||||
/// From where to load the templates
|
||||
///
|
||||
/// This is relative to the current working directory, *not* the config
|
||||
/// file
|
||||
#[serde(default = "default_path", skip_serializing_if = "is_default_path")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/to/templates")]
|
||||
pub path: Utf8PathBuf,
|
||||
|
||||
/// Path to the assets manifest
|
||||
/// Path to the frontend assets manifest file
|
||||
#[serde(
|
||||
default = "default_assets_path",
|
||||
skip_serializing_if = "is_default_assets_path"
|
||||
)]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/to/manifest.json")]
|
||||
pub assets_manifest: Utf8PathBuf,
|
||||
|
||||
/// Path to the translations
|
||||
/// From where to load the translation files
|
||||
///
|
||||
/// The default depends on how the service is distributed:
|
||||
/// - Docker distribution: `/usr/local/share/mas-cli/translations/`
|
||||
/// - pre-built binaries: `./share/translations/`
|
||||
/// - locally-built binaries: `./translations/`
|
||||
#[serde(
|
||||
default = "default_translations_path",
|
||||
skip_serializing_if = "is_default_translations_path"
|
||||
)]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/to/translations")]
|
||||
pub translations_path: Utf8PathBuf,
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,17 @@ use url::Url;
|
||||
|
||||
use crate::{ClientSecret, ClientSecretRaw, ConfigurationSection};
|
||||
|
||||
/// Upstream OAuth 2.0 providers configuration
|
||||
/// Settings related to upstream OAuth 2.0/OIDC providers.
|
||||
/// Additions and modifications within this section are synced with the database
|
||||
/// on server startup. Removed entries are only removed with the [`config sync
|
||||
/// --prune`](./cli/config.md#config-sync---prune---dry-run) command.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
|
||||
pub struct UpstreamOAuth2Config {
|
||||
/// List of OAuth 2.0 providers
|
||||
/// A list of upstream OAuth 2.0/OIDC providers to use to authenticate
|
||||
/// users.
|
||||
///
|
||||
/// Sample configurations for popular providers can be found in the
|
||||
/// [upstream provider setup](../setup/sso.md#sample-configurations) guide.
|
||||
pub providers: Vec<Provider>,
|
||||
}
|
||||
|
||||
@@ -160,14 +167,12 @@ impl ConfigurationSection for UpstreamOAuth2Config {
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ResponseMode {
|
||||
/// `query`: The provider will send the response as a query string in the
|
||||
/// URL search parameters
|
||||
/// The provider will send the response as a query string in the URL search
|
||||
/// parameters. This is the default.
|
||||
Query,
|
||||
|
||||
/// `form_post`: The provider will send the response as a POST request with
|
||||
/// the response parameters in the request body
|
||||
///
|
||||
/// <https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html>
|
||||
/// The provider will send the response as a POST request with the response
|
||||
/// parameters in the request body
|
||||
FormPost,
|
||||
}
|
||||
|
||||
@@ -175,26 +180,24 @@ pub enum ResponseMode {
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TokenAuthMethod {
|
||||
/// `none`: No authentication
|
||||
/// No authentication
|
||||
None,
|
||||
|
||||
/// `client_secret_basic`: `client_id` and `client_secret` used as basic
|
||||
/// authorization credentials
|
||||
/// `client_id` and `client_secret` used as basic authorization credentials
|
||||
ClientSecretBasic,
|
||||
|
||||
/// `client_secret_post`: `client_id` and `client_secret` sent in the
|
||||
/// request body
|
||||
/// `client_id` and `client_secret` sent in the request body
|
||||
ClientSecretPost,
|
||||
|
||||
/// `client_secret_jwt`: a `client_assertion` sent in the request body and
|
||||
/// signed using the `client_secret`
|
||||
/// a `client_assertion` sent in the request body and signed using the
|
||||
/// `client_secret`
|
||||
ClientSecretJwt,
|
||||
|
||||
/// `private_key_jwt`: a `client_assertion` sent in the request body and
|
||||
/// signed by an asymmetric key
|
||||
/// a `client_assertion` sent in the request body and signed by an
|
||||
/// asymmetric key, using the keys defined in the `secrets.keys` section
|
||||
PrivateKeyJwt,
|
||||
|
||||
/// `sign_in_with_apple`: a special method for Signin with Apple
|
||||
/// a special authentication method for Sign-in with Apple
|
||||
SignInWithApple,
|
||||
}
|
||||
|
||||
@@ -227,19 +230,20 @@ impl ImportAction {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OnConflict {
|
||||
/// Fails the upstream OAuth 2.0 login on conflict
|
||||
/// Fails the upstream OAuth 2.0 login.
|
||||
#[default]
|
||||
Fail,
|
||||
|
||||
/// Adds the upstream OAuth 2.0 identity link, regardless of whether there
|
||||
/// is an existing link or not
|
||||
/// Adds the upstream account link to the existing user, regardless of
|
||||
/// whether there is an existing link or not.
|
||||
Add,
|
||||
|
||||
/// Replace any existing upstream OAuth 2.0 identity link
|
||||
/// Replace any existing upstream OAuth 2.0 identity link for this provider
|
||||
/// on the matching user.
|
||||
Replace,
|
||||
|
||||
/// Adds the upstream OAuth 2.0 identity link *only* if there is no existing
|
||||
/// link for this provider on the matching user
|
||||
/// Adds the upstream account link *only* if there is no existing link for
|
||||
/// this provider on the matching user.
|
||||
Set,
|
||||
}
|
||||
|
||||
@@ -257,6 +261,7 @@ pub struct SubjectImportPreference {
|
||||
///
|
||||
/// If not provided, the default template is `{{ user.sub }}`
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"{{ user.sub }}", extend("x-doc" = {"commented": true}))]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
@@ -271,16 +276,19 @@ impl SubjectImportPreference {
|
||||
pub struct LocalpartImportPreference {
|
||||
/// How to handle the attribute
|
||||
#[serde(default, skip_serializing_if = "ImportAction::is_default")]
|
||||
#[schemars(example = &ImportAction::Force, extend("x-doc" = {"commented": true}))]
|
||||
pub action: ImportAction,
|
||||
|
||||
/// The Jinja2 template to use for the localpart attribute
|
||||
///
|
||||
/// If not provided, the default template is `{{ user.preferred_username }}`
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"{{ user.preferred_username }}", extend("x-doc" = {"commented": true}))]
|
||||
pub template: Option<String>,
|
||||
|
||||
/// How to handle conflicts on the claim, default value is `Fail`
|
||||
/// How to handle when localpart already exists.
|
||||
#[serde(default, skip_serializing_if = "OnConflict::is_default")]
|
||||
#[schemars(example = &OnConflict::Fail, extend("x-doc" = {"commented": true}))]
|
||||
pub on_conflict: OnConflict,
|
||||
}
|
||||
|
||||
@@ -295,12 +303,14 @@ impl LocalpartImportPreference {
|
||||
pub struct DisplaynameImportPreference {
|
||||
/// How to handle the attribute
|
||||
#[serde(default, skip_serializing_if = "ImportAction::is_default")]
|
||||
#[schemars(example = &ImportAction::Suggest, extend("x-doc" = {"commented": true}))]
|
||||
pub action: ImportAction,
|
||||
|
||||
/// The Jinja2 template to use for the displayname attribute
|
||||
///
|
||||
/// If not provided, the default template is `{{ user.name }}`
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"{{ user.name }}", extend("x-doc" = {"commented": true}))]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
@@ -315,12 +325,14 @@ impl DisplaynameImportPreference {
|
||||
pub struct EmailImportPreference {
|
||||
/// How to handle the claim
|
||||
#[serde(default, skip_serializing_if = "ImportAction::is_default")]
|
||||
#[schemars(example = &ImportAction::Suggest, extend("x-doc" = {"commented": true}))]
|
||||
pub action: ImportAction,
|
||||
|
||||
/// The Jinja2 template to use for the email address attribute
|
||||
///
|
||||
/// If not provided, the default template is `{{ user.email }}`
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"{{ user.email }}", extend("x-doc" = {"commented": true}))]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
@@ -338,6 +350,7 @@ pub struct AccountNameImportPreference {
|
||||
///
|
||||
/// If not provided, it will be ignored.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"@{{ user.preferred_username }}", extend("x-doc" = {"commented": true}))]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
@@ -350,33 +363,43 @@ impl AccountNameImportPreference {
|
||||
/// How claims should be imported
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct ClaimsImports {
|
||||
/// How to determine the subject of the user
|
||||
/// The subject is an internal identifier used to link the user's provider
|
||||
/// identity to local accounts.
|
||||
/// By default it uses the `sub` claim as per the OIDC spec, which should
|
||||
/// fit most use cases.
|
||||
#[serde(default, skip_serializing_if = "SubjectImportPreference::is_default")]
|
||||
pub subject: SubjectImportPreference,
|
||||
|
||||
/// Whether to skip the interactive screen prompting the user to confirm the
|
||||
/// attributes that are being imported. This requires `localpart.action` to
|
||||
/// be `require` and other attribute actions to be either `ignore`, `force`
|
||||
/// or `require`
|
||||
/// By default, new users will see a screen confirming the attributes they
|
||||
/// are about to have on their account.
|
||||
///
|
||||
/// Setting this to `true` allows skipping this screen, but requires the
|
||||
/// `localpart.action` to be set to `require` and the other attributes
|
||||
/// actions to be set to `ignore`, `force` or `require`.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
#[schemars(example = &false, extend("x-doc" = {"commented": true}))]
|
||||
pub skip_confirmation: bool,
|
||||
|
||||
/// Import the localpart of the MXID
|
||||
/// The localpart is the local part of the user's Matrix ID.
|
||||
/// For example, on the `example.com` server, if the localpart is `alice`,
|
||||
/// the user's Matrix ID will be `@alice:example.com`.
|
||||
#[serde(default, skip_serializing_if = "LocalpartImportPreference::is_default")]
|
||||
pub localpart: LocalpartImportPreference,
|
||||
|
||||
/// Import the displayname of the user.
|
||||
/// The display name is the user's display name.
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "DisplaynameImportPreference::is_default"
|
||||
)]
|
||||
pub displayname: DisplaynameImportPreference,
|
||||
|
||||
/// Import the email address of the user
|
||||
/// An email address to import.
|
||||
#[serde(default, skip_serializing_if = "EmailImportPreference::is_default")]
|
||||
pub email: EmailImportPreference,
|
||||
|
||||
/// Set a human-readable name for the upstream account for display purposes
|
||||
/// An account name, for display purposes only.
|
||||
///
|
||||
/// This helps the end user identify what account they are using
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "AccountNameImportPreference::is_default"
|
||||
@@ -399,14 +422,15 @@ impl ClaimsImports {
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DiscoveryMode {
|
||||
/// Use OIDC discovery with strict metadata verification
|
||||
/// discover the provider through OIDC discovery, with strict metadata
|
||||
/// validation (default)
|
||||
#[default]
|
||||
Oidc,
|
||||
|
||||
/// Use OIDC discovery with relaxed metadata verification
|
||||
/// discover through OIDC discovery, but skip metadata validation
|
||||
Insecure,
|
||||
|
||||
/// Use a static configuration
|
||||
/// don't discover the provider and use the endpoints below
|
||||
Disabled,
|
||||
}
|
||||
|
||||
@@ -422,16 +446,15 @@ impl DiscoveryMode {
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PkceMethod {
|
||||
/// Use PKCE if the provider supports it
|
||||
///
|
||||
/// Defaults to no PKCE if provider discovery is disabled
|
||||
/// use PKCE if the provider supports it (default).
|
||||
/// Determined through discovery, and disabled if discovery is disabled
|
||||
#[default]
|
||||
Auto,
|
||||
|
||||
/// Always use PKCE with the S256 challenge method
|
||||
/// always use PKCE (with the S256 method)
|
||||
Always,
|
||||
|
||||
/// Never use PKCE
|
||||
/// never use PKCE
|
||||
Never,
|
||||
}
|
||||
|
||||
@@ -463,17 +486,20 @@ fn signed_response_alg_default() -> JsonWebSignatureAlg {
|
||||
pub struct SignInWithApple {
|
||||
/// The private key file used to sign the `id_token`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schemars(with = "Option<String>", example = &"/path/to/private.key")]
|
||||
pub private_key_file: Option<Utf8PathBuf>,
|
||||
|
||||
/// The private key used to sign the `id_token`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----")]
|
||||
pub private_key: Option<String>,
|
||||
|
||||
/// The Team ID of the Apple Developer Portal
|
||||
#[schemars(example = &"<team-id>")]
|
||||
pub team_id: String,
|
||||
|
||||
/// The key ID of the Apple Developer Portal
|
||||
#[schemars(example = &"<key-id>")]
|
||||
pub key_id: String,
|
||||
}
|
||||
|
||||
@@ -489,7 +515,7 @@ fn is_default_scope(scope: &str) -> bool {
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OnBackchannelLogout {
|
||||
/// Do nothing
|
||||
/// do nothing, other than validating and logging the request
|
||||
#[default]
|
||||
DoNothing,
|
||||
|
||||
@@ -514,52 +540,48 @@ impl OnBackchannelLogout {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[expect(clippy::struct_excessive_bools)]
|
||||
pub struct Provider {
|
||||
/// Whether this provider is enabled.
|
||||
/// A unique identifier for the provider.
|
||||
///
|
||||
/// Defaults to `true`
|
||||
#[serde(default = "default_true", skip_serializing_if = "is_default_true")]
|
||||
pub enabled: bool,
|
||||
|
||||
/// An internal unique identifier for this provider
|
||||
/// Must be a valid ULID
|
||||
#[schemars(
|
||||
with = "String",
|
||||
regex(pattern = r"^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$"),
|
||||
description = "A ULID as per https://github.com/ulid/spec"
|
||||
description = "A unique identifier for the provider.\n\nMust be a valid ULID",
|
||||
example = &"01HFVBY12TMNTYTBV8W921M5FA"
|
||||
)]
|
||||
pub id: Ulid,
|
||||
|
||||
/// Whether this provider is enabled. Defaults to `true`.
|
||||
#[serde(default = "default_true", skip_serializing_if = "is_default_true")]
|
||||
#[schemars(example = &true, extend("x-doc" = {"commented": true}))]
|
||||
pub enabled: bool,
|
||||
|
||||
/// The ID of the provider that was used by Synapse.
|
||||
/// In order to perform a Synapse-to-MAS migration, this must be specified.
|
||||
///
|
||||
/// ## For providers that used OAuth 2.0 or OpenID Connect in Synapse
|
||||
///
|
||||
/// ### For `oidc_providers`:
|
||||
/// This should be specified as `oidc-` followed by the ID that was
|
||||
/// configured as `idp_id` in one of the `oidc_providers` in the Synapse
|
||||
/// configuration.
|
||||
/// For example, if Synapse's configuration contained `idp_id: wombat` for
|
||||
/// this provider, then specify `oidc-wombat` here.
|
||||
///
|
||||
/// ### For `oidc_config` (legacy):
|
||||
/// Specify `oidc` here.
|
||||
/// Only required when performing a Synapse-to-MAS migration.
|
||||
/// For Synapse's `oidc_providers`, this is `oidc-<idp_id>`; for the legacy
|
||||
/// `oidc_config`, this is `oidc`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"oidc-github", extend("x-doc" = {"commented": true}))]
|
||||
pub synapse_idp_id: Option<String>,
|
||||
|
||||
/// The OIDC issuer URL
|
||||
///
|
||||
/// This is required if OIDC discovery is enabled (which is the default)
|
||||
/// The issuer URL, which will be used to discover the provider's
|
||||
/// configuration. If discovery is enabled, this *must* exactly match the
|
||||
/// `issuer` field advertised in
|
||||
/// `<issuer>/.well-known/openid-configuration`. It must be set if OIDC
|
||||
/// discovery is enabled (which is the default).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"https://example.com/", extend("x-doc" = {"commented": true}))]
|
||||
pub issuer: Option<String>,
|
||||
|
||||
/// A human-readable name for the provider, that will be shown to users
|
||||
/// A human-readable name for the provider, which will be displayed on the
|
||||
/// login page
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"Example", extend("x-doc" = {"commented": true}))]
|
||||
pub human_name: Option<String>,
|
||||
|
||||
/// A brand identifier used to customise the UI, e.g. `apple`, `google`,
|
||||
/// `github`, etc.
|
||||
///
|
||||
/// Values supported by the default template are:
|
||||
///
|
||||
/// A brand identifier for the provider, which will be used to display a
|
||||
/// logo on the login page. Values supported by the default template
|
||||
/// are:
|
||||
/// - `apple`
|
||||
/// - `google`
|
||||
/// - `facebook`
|
||||
@@ -568,9 +590,11 @@ pub struct Provider {
|
||||
/// - `twitter`
|
||||
/// - `discord`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"google", extend("x-doc" = {"commented": true}))]
|
||||
pub brand_name: Option<String>,
|
||||
|
||||
/// The client ID to use when authenticating with the provider
|
||||
/// The client ID to use to authenticate to the provider
|
||||
#[schemars(example = &"mas-fb3f0c09c4c23de4")]
|
||||
pub client_id: String,
|
||||
|
||||
/// The client secret to use when authenticating with the provider
|
||||
@@ -583,10 +607,12 @@ pub struct Provider {
|
||||
pub client_secret: Option<ClientSecret>,
|
||||
|
||||
/// The method to authenticate the client with the provider
|
||||
#[schemars(example = &TokenAuthMethod::ClientSecretPost)]
|
||||
pub token_endpoint_auth_method: TokenAuthMethod,
|
||||
|
||||
/// Additional parameters for the `sign_in_with_apple` method
|
||||
/// Additional parameters for the `sign_in_with_apple` authentication method
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(extend("x-doc" = {"commented": true}))]
|
||||
pub sign_in_with_apple: Option<SignInWithApple>,
|
||||
|
||||
/// The JWS algorithm to use when authenticating the client with the
|
||||
@@ -594,6 +620,7 @@ pub struct Provider {
|
||||
///
|
||||
/// Used by the `client_secret_jwt` and `private_key_jwt` methods
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &JsonWebSignatureAlg::Rs256, extend("x-doc" = {"commented": true}))]
|
||||
pub token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
|
||||
|
||||
/// Expected signature for the JWT payload returned by the token
|
||||
@@ -604,34 +631,32 @@ pub struct Provider {
|
||||
default = "signed_response_alg_default",
|
||||
skip_serializing_if = "is_signed_response_alg_default"
|
||||
)]
|
||||
#[schemars(example = &JsonWebSignatureAlg::Rs256, extend("x-doc" = {"commented": true}))]
|
||||
pub id_token_signed_response_alg: JsonWebSignatureAlg,
|
||||
|
||||
/// The scopes to request from the provider
|
||||
/// The scopes to request from the provider.
|
||||
///
|
||||
/// Defaults to `openid`.
|
||||
/// In most cases, it should always include the `openid` scope
|
||||
#[serde(default = "default_scope", skip_serializing_if = "is_default_scope")]
|
||||
#[schemars(example = &"openid email profile")]
|
||||
pub scope: String,
|
||||
|
||||
/// How to discover the provider's configuration
|
||||
///
|
||||
/// Defaults to `oidc`, which uses OIDC discovery with strict metadata
|
||||
/// verification
|
||||
/// How the provider configuration and endpoints should be discovered
|
||||
#[serde(default, skip_serializing_if = "DiscoveryMode::is_default")]
|
||||
#[schemars(example = &DiscoveryMode::Oidc, extend("x-doc" = {"commented": true}))]
|
||||
pub discovery_mode: DiscoveryMode,
|
||||
|
||||
/// Whether to use proof key for code exchange (PKCE) when requesting and
|
||||
/// exchanging the token.
|
||||
///
|
||||
/// Defaults to `auto`, which uses PKCE if the provider supports it.
|
||||
/// Whether PKCE should be used during the authorization code flow.
|
||||
#[serde(default, skip_serializing_if = "PkceMethod::is_default")]
|
||||
#[schemars(example = &PkceMethod::Auto, extend("x-doc" = {"commented": true}))]
|
||||
pub pkce_method: PkceMethod,
|
||||
|
||||
/// Whether to fetch the user profile from the userinfo endpoint,
|
||||
/// or to rely on the data returned in the `id_token` from the
|
||||
/// `token_endpoint`.
|
||||
/// Whether to fetch user claims from the userinfo endpoint.
|
||||
///
|
||||
/// Defaults to `false`.
|
||||
/// This is disabled by default, as most providers will return the necessary
|
||||
/// claims in the `id_token`
|
||||
#[serde(default)]
|
||||
#[schemars(example = &true, extend("x-doc" = {"commented": true}))]
|
||||
pub fetch_userinfo: bool,
|
||||
|
||||
/// Expected signature for the JWT payload returned by the userinfo
|
||||
@@ -640,111 +665,111 @@ pub struct Provider {
|
||||
/// If not specified, the response is expected to be an unsigned JSON
|
||||
/// payload.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &JsonWebSignatureAlg::Rs256, extend("x-doc" = {"commented": true}))]
|
||||
pub userinfo_signed_response_alg: Option<JsonWebSignatureAlg>,
|
||||
|
||||
/// The URL to use for the provider's authorization endpoint
|
||||
/// The userinfo endpoint.
|
||||
///
|
||||
/// Defaults to the `authorization_endpoint` provided through discovery
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub authorization_endpoint: Option<Url>,
|
||||
|
||||
/// The URL to use for the provider's userinfo endpoint
|
||||
///
|
||||
/// Defaults to the `userinfo_endpoint` provided through discovery
|
||||
/// This takes precedence over the discovery mechanism
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"https://example.com/oauth2/userinfo", extend("x-doc" = {"commented": true}))]
|
||||
pub userinfo_endpoint: Option<Url>,
|
||||
|
||||
/// The URL to use for the provider's token endpoint
|
||||
/// The provider authorization endpoint.
|
||||
///
|
||||
/// Defaults to the `token_endpoint` provided through discovery
|
||||
/// This takes precedence over the discovery mechanism
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"https://example.com/oauth2/authorize", extend("x-doc" = {"commented": true}))]
|
||||
pub authorization_endpoint: Option<Url>,
|
||||
|
||||
/// The provider token endpoint.
|
||||
///
|
||||
/// This takes precedence over the discovery mechanism
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"https://example.com/oauth2/token", extend("x-doc" = {"commented": true}))]
|
||||
pub token_endpoint: Option<Url>,
|
||||
|
||||
/// The URL to use for getting the provider's public keys
|
||||
/// The provider JWKS URI.
|
||||
///
|
||||
/// Defaults to the `jwks_uri` provided through discovery
|
||||
/// This takes precedence over the discovery mechanism
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &"https://example.com/oauth2/keys", extend("x-doc" = {"commented": true}))]
|
||||
pub jwks_uri: Option<Url>,
|
||||
|
||||
/// The response mode we ask the provider to use for the callback
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(example = &ResponseMode::Query, extend("x-doc" = {"commented": true}))]
|
||||
pub response_mode: Option<ResponseMode>,
|
||||
|
||||
/// How claims should be imported from the `id_token` provided by the
|
||||
/// provider
|
||||
#[serde(default, skip_serializing_if = "ClaimsImports::is_default")]
|
||||
pub claims_imports: ClaimsImports,
|
||||
|
||||
/// Additional parameters to include in the authorization request.
|
||||
///
|
||||
/// Each value is a [`MiniJinja`] template. The template context
|
||||
/// exposes:
|
||||
/// Values are Jinja2 templates. The template context exposes:
|
||||
///
|
||||
/// - `params`: a map containing the raw query parameters from the
|
||||
/// downstream authorization request. The map is empty when the upstream
|
||||
/// login was not initiated by a downstream OAuth/OIDC authorization
|
||||
/// request (e.g. account linking, direct login from the login page).
|
||||
/// - `params`: a map containing the raw query parameters of the downstream
|
||||
/// authorization request (empty when the upstream login was not
|
||||
/// triggered by a downstream authorization request, e.g. account linking
|
||||
/// or direct login).
|
||||
/// - `logged_out`: a boolean that is `true` when the browser recently
|
||||
/// signed out of MAS and has no active session. This lets you force a
|
||||
/// fresh prompt at the upstream provider after sign-out — otherwise a
|
||||
/// fresh prompt at the upstream provider after sign-out — e.g.
|
||||
/// `prompt: "{% if logged_out %}login{% endif %}"` — otherwise a
|
||||
/// provider that still has a live session would silently sign the user
|
||||
/// back in.
|
||||
///
|
||||
/// [`MiniJinja`]: https://docs.rs/minijinja
|
||||
/// Templates that render to an empty string are dropped rather than
|
||||
/// forwarded.
|
||||
///
|
||||
/// Templates that render to an empty string are dropped — so
|
||||
/// referencing a downstream parameter that wasn't supplied (e.g.
|
||||
/// `{{ params.login_hint }}`) results in no parameter being
|
||||
/// forwarded, rather than an empty one.
|
||||
///
|
||||
/// Plain strings (without `{{ … }}`) are valid templates that render
|
||||
/// to themselves.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```yaml
|
||||
/// additional_authorization_parameters:
|
||||
/// login_hint: "{{ params.login_hint }}"
|
||||
/// acr_values: "{{ params.acr_values }}"
|
||||
/// kc_idp_hint: "saml"
|
||||
/// # Force re-authentication at the upstream provider after sign-out.
|
||||
/// # Keycloak only supports `prompt=login`, not `prompt=select_account`.
|
||||
/// prompt: "{% if logged_out %}login{% endif %}"
|
||||
/// ```
|
||||
///
|
||||
/// `params` exposes the entire raw query string of the downstream
|
||||
/// request (including `client_id`, `state`, `code_challenge`, …).
|
||||
/// Forward specific keys deliberately; don't blindly proxy the
|
||||
/// whole map.
|
||||
///
|
||||
/// Order of keys is not preserved.
|
||||
/// Plain strings without `{{ … }}` render to themselves, so static values
|
||||
/// work as expected.
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
#[schemars(example = &serde_json::json!({
|
||||
"foo": "bar",
|
||||
"login_hint": "{{ params.login_hint }}",
|
||||
"acr_values": "{{ params.acr_values }}",
|
||||
}), extend("x-doc" = {"commented": true}))]
|
||||
pub additional_authorization_parameters: BTreeMap<String, String>,
|
||||
|
||||
/// Whether the `login_hint` should be forwarded to the provider in the
|
||||
/// authorization request.
|
||||
///
|
||||
/// Defaults to `false`.
|
||||
///
|
||||
/// Deprecated: prefer adding
|
||||
/// `login_hint: "{{ params.login_hint }}"` to
|
||||
/// `additional_authorization_parameters` instead. When this flag is
|
||||
/// set, a `login_hint` template entry is injected automatically if
|
||||
/// one is not already present.
|
||||
/// Deprecated: prefer adding `login_hint: "{{ params.login_hint }}"` to
|
||||
/// `additional_authorization_parameters` instead. When this flag is set, a
|
||||
/// `login_hint` template entry is injected automatically if one is not
|
||||
/// already present.
|
||||
#[serde(default)]
|
||||
#[schemars(extend("x-doc" = {"commented": true}))]
|
||||
pub forward_login_hint: bool,
|
||||
|
||||
/// What to do when receiving an OIDC Backchannel logout request.
|
||||
///
|
||||
/// Defaults to `do_nothing`.
|
||||
#[serde(default, skip_serializing_if = "OnBackchannelLogout::is_default")]
|
||||
#[schemars(example = &OnBackchannelLogout::DoNothing, extend("x-doc" = {"commented": true}))]
|
||||
pub on_backchannel_logout: OnBackchannelLogout,
|
||||
|
||||
/// Whether or not to require a registration token on `OAuth2` auth
|
||||
///
|
||||
/// Defaults to `false`
|
||||
/// Whether a registration token is required to register through this
|
||||
/// provider. Defaults to `false`.
|
||||
#[serde(default)]
|
||||
#[schemars(extend("x-doc" = {"commented": true}))]
|
||||
pub registration_token_required: bool,
|
||||
|
||||
/// How user attributes should be mapped
|
||||
///
|
||||
/// Most of those attributes have two main properties:
|
||||
/// - `action`: what to do with the attribute. Possible values are:
|
||||
/// - `ignore`: ignore the attribute
|
||||
/// - `suggest`: suggest the attribute to the user, but let them opt
|
||||
/// out
|
||||
/// - `force`: always import the attribute, and don't fail if it's
|
||||
/// missing
|
||||
/// - `require`: always import the attribute, and fail if it's missing
|
||||
/// - `template`: a Jinja2 template used to generate the value. In this
|
||||
/// template, the `user` variable is available, which contains the
|
||||
/// user's attributes retrieved from the `id_token` given by the
|
||||
/// upstream provider and/or through the userinfo endpoint.
|
||||
///
|
||||
/// Each attribute has a default template which follows the well-known OIDC
|
||||
/// claims.
|
||||
#[serde(default, skip_serializing_if = "ClaimsImports::is_default")]
|
||||
pub claims_imports: ClaimsImports,
|
||||
}
|
||||
|
||||
impl Provider {
|
||||
|
||||
Reference in New Issue
Block a user