You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-06 06:02:40 +03:00
Flatten the upstream_oauth2 config section
This commit is contained in:
@@ -24,7 +24,7 @@ use sqlx::{postgres::PgAdvisoryLock, Connection, PgConnection};
|
||||
use tracing::{error, info, info_span, warn};
|
||||
|
||||
fn map_import_action(
|
||||
config: &mas_config::UpstreamOAuth2ImportAction,
|
||||
config: mas_config::UpstreamOAuth2ImportAction,
|
||||
) -> mas_data_model::UpstreamOAuthProviderImportAction {
|
||||
match config {
|
||||
mas_config::UpstreamOAuth2ImportAction::Ignore => {
|
||||
@@ -50,15 +50,15 @@ fn map_claims_imports(
|
||||
template: config.subject.template.clone(),
|
||||
},
|
||||
localpart: mas_data_model::UpstreamOAuthProviderImportPreference {
|
||||
action: map_import_action(&config.localpart.action),
|
||||
action: map_import_action(config.localpart.action),
|
||||
template: config.localpart.template.clone(),
|
||||
},
|
||||
displayname: mas_data_model::UpstreamOAuthProviderImportPreference {
|
||||
action: map_import_action(&config.displayname.action),
|
||||
action: map_import_action(config.displayname.action),
|
||||
template: config.displayname.template.clone(),
|
||||
},
|
||||
email: mas_data_model::UpstreamOAuthProviderImportPreference {
|
||||
action: map_import_action(&config.email.action),
|
||||
action: map_import_action(config.email.action),
|
||||
template: config.email.template.clone(),
|
||||
},
|
||||
verify_email: match config.email.set_email_verification {
|
||||
@@ -145,11 +145,10 @@ pub async fn config_sync(
|
||||
}
|
||||
|
||||
let encrypted_client_secret = provider
|
||||
.client_secret()
|
||||
.client_secret
|
||||
.as_deref()
|
||||
.map(|client_secret| encrypter.encrypt_to_string(client_secret.as_bytes()))
|
||||
.transpose()?;
|
||||
let token_endpoint_auth_method = provider.client_auth_method();
|
||||
let token_endpoint_signing_alg = provider.client_auth_signing_alg();
|
||||
|
||||
let discovery_mode = match provider.discovery_mode {
|
||||
mas_config::UpstreamOAuth2DiscoveryMode::Oidc => {
|
||||
@@ -198,8 +197,10 @@ pub async fn config_sync(
|
||||
human_name: provider.human_name,
|
||||
brand_name: provider.brand_name,
|
||||
scope: provider.scope.parse()?,
|
||||
token_endpoint_auth_method,
|
||||
token_endpoint_signing_alg,
|
||||
token_endpoint_auth_method: provider.token_endpoint_auth_method.into(),
|
||||
token_endpoint_signing_alg: provider
|
||||
.token_endpoint_auth_signing_alg
|
||||
.clone(),
|
||||
client_id: provider.client_id,
|
||||
encrypted_client_secret,
|
||||
claims_imports: map_claims_imports(&provider.claims_imports),
|
||||
|
@@ -53,8 +53,7 @@ pub use self::{
|
||||
upstream_oauth2::{
|
||||
ClaimsImports as UpstreamOAuth2ClaimsImports, DiscoveryMode as UpstreamOAuth2DiscoveryMode,
|
||||
EmailImportPreference as UpstreamOAuth2EmailImportPreference,
|
||||
ImportAction as UpstreamOAuth2ImportAction,
|
||||
ImportPreference as UpstreamOAuth2ImportPreference, PkceMethod as UpstreamOAuth2PkceMethod,
|
||||
ImportAction as UpstreamOAuth2ImportAction, PkceMethod as UpstreamOAuth2PkceMethod,
|
||||
SetEmailVerification as UpstreamOAuth2SetEmailVerification, UpstreamOAuth2Config,
|
||||
},
|
||||
};
|
||||
|
@@ -12,13 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{collections::BTreeMap, ops::Deref};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod};
|
||||
use rand::Rng;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de::Error, Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use ulid::Ulid;
|
||||
use url::Url;
|
||||
@@ -43,42 +43,104 @@ impl ConfigurationSection for UpstreamOAuth2Config {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
|
||||
for (index, provider) in self.providers.iter().enumerate() {
|
||||
let annotate = |mut error: figment::Error| {
|
||||
error.metadata = figment
|
||||
.find_metadata(&format!("{root}.providers", root = Self::PATH.unwrap()))
|
||||
.cloned();
|
||||
error.profile = Some(figment::Profile::Default);
|
||||
error.path = vec![
|
||||
Self::PATH.unwrap().to_owned(),
|
||||
"providers".to_owned(),
|
||||
index.to_string(),
|
||||
];
|
||||
Err(error)
|
||||
};
|
||||
|
||||
match provider.token_endpoint_auth_method {
|
||||
TokenAuthMethod::None | TokenAuthMethod::PrivateKeyJwt => {
|
||||
if provider.client_secret.is_some() {
|
||||
return annotate(figment::Error::custom("Unexpected field `client_secret` for the selected authentication method"));
|
||||
}
|
||||
}
|
||||
TokenAuthMethod::ClientSecretBasic
|
||||
| TokenAuthMethod::ClientSecretPost
|
||||
| TokenAuthMethod::ClientSecretJwt => {
|
||||
if provider.client_secret.is_none() {
|
||||
return annotate(figment::Error::missing_field("client_secret"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match provider.token_endpoint_auth_method {
|
||||
TokenAuthMethod::None
|
||||
| TokenAuthMethod::ClientSecretBasic
|
||||
| TokenAuthMethod::ClientSecretPost => {
|
||||
if provider.token_endpoint_auth_signing_alg.is_some() {
|
||||
return annotate(figment::Error::custom(
|
||||
"Unexpected field `token_endpoint_auth_signing_alg` for the selected authentication method",
|
||||
));
|
||||
}
|
||||
}
|
||||
TokenAuthMethod::ClientSecretJwt | TokenAuthMethod::PrivateKeyJwt => {
|
||||
if provider.token_endpoint_auth_signing_alg.is_none() {
|
||||
return annotate(figment::Error::missing_field(
|
||||
"token_endpoint_auth_signing_alg",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Authentication methods used against the OAuth 2.0 provider
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(tag = "token_endpoint_auth_method", rename_all = "snake_case")]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TokenAuthMethod {
|
||||
/// `none`: No authentication
|
||||
None,
|
||||
|
||||
/// `client_secret_basic`: `client_id` and `client_secret` used as basic
|
||||
/// authorization credentials
|
||||
ClientSecretBasic { client_secret: String },
|
||||
ClientSecretBasic,
|
||||
|
||||
/// `client_secret_post`: `client_id` and `client_secret` sent in the
|
||||
/// request body
|
||||
ClientSecretPost { client_secret: String },
|
||||
ClientSecretPost,
|
||||
|
||||
/// `client_secret_basic`: a `client_assertion` sent in the request body and
|
||||
/// `client_secret_jwt`: a `client_assertion` sent in the request body and
|
||||
/// signed using the `client_secret`
|
||||
ClientSecretJwt {
|
||||
client_secret: String,
|
||||
token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
|
||||
},
|
||||
ClientSecretJwt,
|
||||
|
||||
/// `client_secret_basic`: a `client_assertion` sent in the request body and
|
||||
/// `private_key_jwt`: a `client_assertion` sent in the request body and
|
||||
/// signed by an asymmetric key
|
||||
PrivateKeyJwt {
|
||||
token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
|
||||
},
|
||||
PrivateKeyJwt,
|
||||
}
|
||||
|
||||
impl From<TokenAuthMethod> for OAuthClientAuthenticationMethod {
|
||||
fn from(method: TokenAuthMethod) -> Self {
|
||||
match method {
|
||||
TokenAuthMethod::None => OAuthClientAuthenticationMethod::None,
|
||||
TokenAuthMethod::ClientSecretBasic => {
|
||||
OAuthClientAuthenticationMethod::ClientSecretBasic
|
||||
}
|
||||
TokenAuthMethod::ClientSecretPost => OAuthClientAuthenticationMethod::ClientSecretPost,
|
||||
TokenAuthMethod::ClientSecretJwt => OAuthClientAuthenticationMethod::ClientSecretJwt,
|
||||
TokenAuthMethod::PrivateKeyJwt => OAuthClientAuthenticationMethod::PrivateKeyJwt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// How to handle a claim
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ImportAction {
|
||||
/// Ignore the claim
|
||||
@@ -95,16 +157,15 @@ pub enum ImportAction {
|
||||
Require,
|
||||
}
|
||||
|
||||
/// What should be done with a attribute
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct ImportPreference {
|
||||
/// How to handle the attribute
|
||||
#[serde(default)]
|
||||
pub action: ImportAction,
|
||||
impl ImportAction {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
const fn is_default(&self) -> bool {
|
||||
matches!(self, ImportAction::Ignore)
|
||||
}
|
||||
}
|
||||
|
||||
/// Should the email address be marked as verified
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SetEmailVerification {
|
||||
/// Mark the email address as verified
|
||||
@@ -119,85 +180,130 @@ pub enum SetEmailVerification {
|
||||
Import,
|
||||
}
|
||||
|
||||
impl SetEmailVerification {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
const fn is_default(&self) -> bool {
|
||||
matches!(self, SetEmailVerification::Import)
|
||||
}
|
||||
}
|
||||
|
||||
/// What should be done for the subject attribute
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct SubjectImportPreference {
|
||||
/// The Jinja2 template to use for the subject attribute
|
||||
///
|
||||
/// If not provided, the default template is `{{ user.sub }}`
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
impl SubjectImportPreference {
|
||||
const fn is_default(&self) -> bool {
|
||||
self.template.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// What should be done for the localpart attribute
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct LocalpartImportPreference {
|
||||
/// How to handle the attribute
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "ImportAction::is_default")]
|
||||
pub action: ImportAction,
|
||||
|
||||
/// The Jinja2 template to use for the localpart attribute
|
||||
///
|
||||
/// If not provided, the default template is `{{ user.preferred_username }}`
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
impl LocalpartImportPreference {
|
||||
const fn is_default(&self) -> bool {
|
||||
self.action.is_default() && self.template.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// What should be done for the displayname attribute
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct DisplaynameImportPreference {
|
||||
/// How to handle the attribute
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "ImportAction::is_default")]
|
||||
pub action: ImportAction,
|
||||
|
||||
/// The Jinja2 template to use for the displayname attribute
|
||||
///
|
||||
/// If not provided, the default template is `{{ user.name }}`
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
impl DisplaynameImportPreference {
|
||||
const fn is_default(&self) -> bool {
|
||||
self.action.is_default() && self.template.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// What should be done with the email attribute
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct EmailImportPreference {
|
||||
/// How to handle the claim
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "ImportAction::is_default")]
|
||||
pub action: ImportAction,
|
||||
|
||||
/// The Jinja2 template to use for the email address attribute
|
||||
///
|
||||
/// If not provided, the default template is `{{ user.email }}`
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
|
||||
/// Should the email address be marked as verified
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "SetEmailVerification::is_default")]
|
||||
pub set_email_verification: SetEmailVerification,
|
||||
}
|
||||
|
||||
impl EmailImportPreference {
|
||||
const fn is_default(&self) -> bool {
|
||||
self.action.is_default()
|
||||
&& self.template.is_none()
|
||||
&& self.set_email_verification.is_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "SubjectImportPreference::is_default")]
|
||||
pub subject: SubjectImportPreference,
|
||||
|
||||
/// Import the localpart of the MXID
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "LocalpartImportPreference::is_default")]
|
||||
pub localpart: LocalpartImportPreference,
|
||||
|
||||
/// Import the displayname of the user.
|
||||
#[serde(default)]
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "DisplaynameImportPreference::is_default"
|
||||
)]
|
||||
pub displayname: DisplaynameImportPreference,
|
||||
|
||||
/// Import the email address of the user based on the `email` and
|
||||
/// `email_verified` claims
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "EmailImportPreference::is_default")]
|
||||
pub email: EmailImportPreference,
|
||||
}
|
||||
|
||||
impl ClaimsImports {
|
||||
const fn is_default(&self) -> bool {
|
||||
self.subject.is_default()
|
||||
&& self.localpart.is_default()
|
||||
&& self.displayname.is_default()
|
||||
&& self.email.is_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// How to discover the provider's configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DiscoveryMode {
|
||||
/// Use OIDC discovery with strict metadata verification
|
||||
@@ -211,9 +317,16 @@ pub enum DiscoveryMode {
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl DiscoveryMode {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
const fn is_default(&self) -> bool {
|
||||
matches!(self, DiscoveryMode::Oidc)
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to use proof key for code exchange (PKCE) when requesting and
|
||||
/// exchanging the token.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PkceMethod {
|
||||
/// Use PKCE if the provider supports it
|
||||
@@ -229,6 +342,13 @@ pub enum PkceMethod {
|
||||
Never,
|
||||
}
|
||||
|
||||
impl PkceMethod {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
const fn is_default(&self) -> bool {
|
||||
matches!(self, PkceMethod::Auto)
|
||||
}
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct Provider {
|
||||
@@ -244,6 +364,7 @@ pub struct Provider {
|
||||
pub issuer: String,
|
||||
|
||||
/// A human-readable name for the provider, that will be shown to users
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub human_name: Option<String>,
|
||||
|
||||
/// A brand identifier used to customise the UI, e.g. `apple`, `google`,
|
||||
@@ -257,108 +378,72 @@ pub struct Provider {
|
||||
/// - `github`
|
||||
/// - `gitlab`
|
||||
/// - `twitter`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub brand_name: Option<String>,
|
||||
|
||||
/// The client ID to use when authenticating with the provider
|
||||
pub client_id: String,
|
||||
|
||||
/// The client secret to use when authenticating with the provider
|
||||
///
|
||||
/// Used by the `client_secret_basic`, `client_secret_post`, and
|
||||
/// `client_secret_jwt` methods
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub client_secret: Option<String>,
|
||||
|
||||
/// The method to authenticate the client with the provider
|
||||
pub token_endpoint_auth_method: TokenAuthMethod,
|
||||
|
||||
/// The JWS algorithm to use when authenticating the client with the
|
||||
/// provider
|
||||
///
|
||||
/// Used by the `client_secret_jwt` and `private_key_jwt` methods
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
|
||||
|
||||
/// The scopes to request from the provider
|
||||
pub scope: String,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub token_auth_method: TokenAuthMethod,
|
||||
|
||||
/// How to discover the provider's configuration
|
||||
///
|
||||
/// Defaults to use OIDC discovery with strict metadata verification
|
||||
#[serde(default)]
|
||||
/// Defaults to `oidc`, which uses OIDC discovery with strict metadata
|
||||
/// verification
|
||||
#[serde(default, skip_serializing_if = "DiscoveryMode::is_default")]
|
||||
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.
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "PkceMethod::is_default")]
|
||||
pub pkce_method: PkceMethod,
|
||||
|
||||
/// The URL to use for the provider's authorization 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 token endpoint
|
||||
///
|
||||
/// Defaults to the `token_endpoint` provided through discovery
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub token_endpoint: Option<Url>,
|
||||
|
||||
/// The URL to use for getting the provider's public keys
|
||||
///
|
||||
/// Defaults to the `jwks_uri` provided through discovery
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub jwks_uri: Option<Url>,
|
||||
|
||||
/// How claims should be imported from the `id_token` provided by the
|
||||
/// provider
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "ClaimsImports::is_default")]
|
||||
pub claims_imports: ClaimsImports,
|
||||
|
||||
/// Additional parameters to include in the authorization request
|
||||
///
|
||||
/// Orders of the keys are not preserved.
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub additional_authorization_parameters: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl Deref for Provider {
|
||||
type Target = TokenAuthMethod;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.token_auth_method
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenAuthMethod {
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn client_auth_method(&self) -> OAuthClientAuthenticationMethod {
|
||||
match self {
|
||||
TokenAuthMethod::None => OAuthClientAuthenticationMethod::None,
|
||||
TokenAuthMethod::ClientSecretBasic { .. } => {
|
||||
OAuthClientAuthenticationMethod::ClientSecretBasic
|
||||
}
|
||||
TokenAuthMethod::ClientSecretPost { .. } => {
|
||||
OAuthClientAuthenticationMethod::ClientSecretPost
|
||||
}
|
||||
TokenAuthMethod::ClientSecretJwt { .. } => {
|
||||
OAuthClientAuthenticationMethod::ClientSecretJwt
|
||||
}
|
||||
TokenAuthMethod::PrivateKeyJwt { .. } => OAuthClientAuthenticationMethod::PrivateKeyJwt,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn client_secret(&self) -> Option<&str> {
|
||||
match self {
|
||||
TokenAuthMethod::None | TokenAuthMethod::PrivateKeyJwt { .. } => None,
|
||||
TokenAuthMethod::ClientSecretBasic { client_secret }
|
||||
| TokenAuthMethod::ClientSecretPost { client_secret }
|
||||
| TokenAuthMethod::ClientSecretJwt { client_secret, .. } => Some(client_secret),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn client_auth_signing_alg(&self) -> Option<JsonWebSignatureAlg> {
|
||||
match self {
|
||||
TokenAuthMethod::ClientSecretJwt {
|
||||
token_endpoint_auth_signing_alg,
|
||||
..
|
||||
}
|
||||
| TokenAuthMethod::PrivateKeyJwt {
|
||||
token_endpoint_auth_signing_alg,
|
||||
..
|
||||
} => token_endpoint_auth_signing_alg.clone(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1612,108 +1612,13 @@
|
||||
}
|
||||
},
|
||||
"Provider": {
|
||||
"description": "Authentication methods used against the OAuth 2.0 provider",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "`none`: No authentication",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"token_endpoint_auth_method"
|
||||
],
|
||||
"properties": {
|
||||
"token_endpoint_auth_method": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "`client_secret_basic`: `client_id` and `client_secret` used as basic authorization credentials",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"client_secret",
|
||||
"token_endpoint_auth_method"
|
||||
],
|
||||
"properties": {
|
||||
"token_endpoint_auth_method": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"client_secret_basic"
|
||||
]
|
||||
},
|
||||
"client_secret": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "`client_secret_post`: `client_id` and `client_secret` sent in the request body",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"client_secret",
|
||||
"token_endpoint_auth_method"
|
||||
],
|
||||
"properties": {
|
||||
"token_endpoint_auth_method": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"client_secret_post"
|
||||
]
|
||||
},
|
||||
"client_secret": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "`client_secret_basic`: a `client_assertion` sent in the request body and signed using the `client_secret`",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"client_secret",
|
||||
"token_endpoint_auth_method"
|
||||
],
|
||||
"properties": {
|
||||
"token_endpoint_auth_method": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"client_secret_jwt"
|
||||
]
|
||||
},
|
||||
"client_secret": {
|
||||
"type": "string"
|
||||
},
|
||||
"token_endpoint_auth_signing_alg": {
|
||||
"$ref": "#/definitions/JsonWebSignatureAlg"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "`client_secret_basic`: a `client_assertion` sent in the request body and signed by an asymmetric key",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"token_endpoint_auth_method"
|
||||
],
|
||||
"properties": {
|
||||
"token_endpoint_auth_method": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"private_key_jwt"
|
||||
]
|
||||
},
|
||||
"token_endpoint_auth_signing_alg": {
|
||||
"$ref": "#/definitions/JsonWebSignatureAlg"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [
|
||||
"client_id",
|
||||
"id",
|
||||
"issuer",
|
||||
"scope"
|
||||
"scope",
|
||||
"token_endpoint_auth_method"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -1737,13 +1642,32 @@
|
||||
"description": "The client ID to use when authenticating with the provider",
|
||||
"type": "string"
|
||||
},
|
||||
"client_secret": {
|
||||
"description": "The client secret to use when authenticating with the provider\n\nUsed by the `client_secret_basic`, `client_secret_post`, and `client_secret_jwt` methods",
|
||||
"type": "string"
|
||||
},
|
||||
"token_endpoint_auth_method": {
|
||||
"description": "The method to authenticate the client with the provider",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TokenAuthMethod"
|
||||
}
|
||||
]
|
||||
},
|
||||
"token_endpoint_auth_signing_alg": {
|
||||
"description": "The JWS algorithm to use when authenticating the client with the provider\n\nUsed by the `client_secret_jwt` and `private_key_jwt` methods",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/JsonWebSignatureAlg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scope": {
|
||||
"description": "The scopes to request from the provider",
|
||||
"type": "string"
|
||||
},
|
||||
"discovery_mode": {
|
||||
"description": "How to discover the provider's configuration\n\nDefaults to use OIDC discovery with strict metadata verification",
|
||||
"default": "oidc",
|
||||
"description": "How to discover the provider's configuration\n\nDefaults to `oidc`, which uses OIDC discovery with strict metadata verification",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DiscoveryMode"
|
||||
@@ -1752,7 +1676,6 @@
|
||||
},
|
||||
"pkce_method": {
|
||||
"description": "Whether to use proof key for code exchange (PKCE) when requesting and exchanging the token.\n\nDefaults to `auto`, which uses PKCE if the provider supports it.",
|
||||
"default": "auto",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PkceMethod"
|
||||
@@ -1776,24 +1699,6 @@
|
||||
},
|
||||
"claims_imports": {
|
||||
"description": "How claims should be imported from the `id_token` provided by the provider",
|
||||
"default": {
|
||||
"subject": {
|
||||
"template": null
|
||||
},
|
||||
"localpart": {
|
||||
"action": "ignore",
|
||||
"template": null
|
||||
},
|
||||
"displayname": {
|
||||
"action": "ignore",
|
||||
"template": null
|
||||
},
|
||||
"email": {
|
||||
"action": "ignore",
|
||||
"template": null,
|
||||
"set_email_verification": "import"
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ClaimsImports"
|
||||
@@ -1802,7 +1707,6 @@
|
||||
},
|
||||
"additional_authorization_parameters": {
|
||||
"description": "Additional parameters to include in the authorization request\n\nOrders of the keys are not preserved.",
|
||||
"default": {},
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
@@ -1810,6 +1714,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"TokenAuthMethod": {
|
||||
"description": "Authentication methods used against the OAuth 2.0 provider",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "`none`: No authentication",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "`client_secret_basic`: `client_id` and `client_secret` used as basic authorization credentials",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"client_secret_basic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "`client_secret_post`: `client_id` and `client_secret` sent in the request body",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"client_secret_post"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "`client_secret_jwt`: a `client_assertion` sent in the request body and signed using the `client_secret`",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"client_secret_jwt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "`private_key_jwt`: a `client_assertion` sent in the request body and signed by an asymmetric key",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"private_key_jwt"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"DiscoveryMode": {
|
||||
"description": "How to discover the provider's configuration",
|
||||
"oneOf": [
|
||||
@@ -1868,9 +1812,6 @@
|
||||
"properties": {
|
||||
"subject": {
|
||||
"description": "How to determine the subject of the user",
|
||||
"default": {
|
||||
"template": null
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/SubjectImportPreference"
|
||||
@@ -1879,10 +1820,6 @@
|
||||
},
|
||||
"localpart": {
|
||||
"description": "Import the localpart of the MXID",
|
||||
"default": {
|
||||
"action": "ignore",
|
||||
"template": null
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/LocalpartImportPreference"
|
||||
@@ -1891,10 +1828,6 @@
|
||||
},
|
||||
"displayname": {
|
||||
"description": "Import the displayname of the user.",
|
||||
"default": {
|
||||
"action": "ignore",
|
||||
"template": null
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DisplaynameImportPreference"
|
||||
@@ -1903,11 +1836,6 @@
|
||||
},
|
||||
"email": {
|
||||
"description": "Import the email address of the user based on the `email` and `email_verified` claims",
|
||||
"default": {
|
||||
"action": "ignore",
|
||||
"template": null,
|
||||
"set_email_verification": "import"
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/EmailImportPreference"
|
||||
@@ -1922,7 +1850,6 @@
|
||||
"properties": {
|
||||
"template": {
|
||||
"description": "The Jinja2 template to use for the subject attribute\n\nIf not provided, the default template is `{{ user.sub }}`",
|
||||
"default": null,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -1933,7 +1860,6 @@
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "How to handle the attribute",
|
||||
"default": "ignore",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ImportAction"
|
||||
@@ -1942,7 +1868,6 @@
|
||||
},
|
||||
"template": {
|
||||
"description": "The Jinja2 template to use for the localpart attribute\n\nIf not provided, the default template is `{{ user.preferred_username }}`",
|
||||
"default": null,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -1986,7 +1911,6 @@
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "How to handle the attribute",
|
||||
"default": "ignore",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ImportAction"
|
||||
@@ -1995,7 +1919,6 @@
|
||||
},
|
||||
"template": {
|
||||
"description": "The Jinja2 template to use for the displayname attribute\n\nIf not provided, the default template is `{{ user.name }}`",
|
||||
"default": null,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2006,7 +1929,6 @@
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "How to handle the claim",
|
||||
"default": "ignore",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ImportAction"
|
||||
@@ -2015,12 +1937,10 @@
|
||||
},
|
||||
"template": {
|
||||
"description": "The Jinja2 template to use for the email address attribute\n\nIf not provided, the default template is `{{ user.email }}`",
|
||||
"default": null,
|
||||
"type": "string"
|
||||
},
|
||||
"set_email_verification": {
|
||||
"description": "Should the email address be marked as verified",
|
||||
"default": "import",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/SetEmailVerification"
|
||||
|
Reference in New Issue
Block a user