mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-09-11 19:53:02 +02:00
chore: Fix most clippy issue, format & typos
This commit is contained in:
parent
67e5869e43
commit
d7b48a0f7c
19 changed files with 369 additions and 409 deletions
|
@ -2,7 +2,6 @@ pub(super) mod account;
|
|||
pub(super) mod account_data;
|
||||
pub(super) mod alias;
|
||||
pub(super) mod appservice;
|
||||
pub(super) mod oidc;
|
||||
pub(super) mod backup;
|
||||
pub(super) mod capabilities;
|
||||
pub(super) mod context;
|
||||
|
@ -14,6 +13,7 @@ pub(super) mod media;
|
|||
pub(super) mod media_legacy;
|
||||
pub(super) mod membership;
|
||||
pub(super) mod message;
|
||||
pub(super) mod oidc;
|
||||
pub(super) mod openid;
|
||||
pub(super) mod presence;
|
||||
pub(super) mod profile;
|
||||
|
@ -45,7 +45,6 @@ pub(super) use account::*;
|
|||
pub(super) use account_data::*;
|
||||
pub(super) use alias::*;
|
||||
pub(super) use appservice::*;
|
||||
pub(super) use oidc::*;
|
||||
pub(super) use backup::*;
|
||||
pub(super) use capabilities::*;
|
||||
pub(super) use context::*;
|
||||
|
@ -58,6 +57,7 @@ pub(super) use media_legacy::*;
|
|||
pub(super) use membership::*;
|
||||
pub use membership::{join_room_by_id_helper, leave_all_rooms, leave_room};
|
||||
pub(super) use message::*;
|
||||
pub(super) use oidc::*;
|
||||
pub(super) use openid::*;
|
||||
pub(super) use presence::*;
|
||||
pub(super) use profile::*;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use conduwuit_web::oidc::{oidc_consent_form, oidc_login_form, AuthorizationQuery, OidcRequest, OidcResponse};
|
||||
use axum::extract::{Query, State};
|
||||
use conduwuit::{Result, err};
|
||||
use conduwuit_web::oidc::{
|
||||
AuthorizationQuery, OidcRequest, OidcResponse, oidc_consent_form, oidc_login_form,
|
||||
};
|
||||
use oxide_auth::{
|
||||
endpoint::{OwnerConsent, Solicitation},
|
||||
frontends::simple::endpoint::FnSolicitor,
|
||||
};
|
||||
use axum::extract::{Query, State};
|
||||
use conduwuit::{Result, err};
|
||||
use percent_encoding::percent_decode_str;
|
||||
|
||||
/// # `GET /_matrix/client/unstable/org.matrix.msc2964/authorize`
|
||||
|
@ -24,12 +26,12 @@ pub(crate) async fn authorize(
|
|||
// Enforce MSC2964's restrictions on OAuth2 flow.
|
||||
let Ok(scope) = percent_decode_str(&query.scope).decode_utf8() else {
|
||||
return Err(err!(Request(Unknown("the scope could not be percent-decoded"))));
|
||||
} ;
|
||||
};
|
||||
//if ! scope.contains("urn:matrix:api:*") {
|
||||
if ! scope.contains("urn:matrix:org.matrix.msc2967.client:api:*") {
|
||||
if !scope.contains("urn:matrix:org.matrix.msc2967.client:api:*") {
|
||||
return Err(err!(Request(Unknown("the scope does not include the client API"))));
|
||||
}
|
||||
if ! scope.contains("urn:matrix:org.matrix.msc2967.client:device:") {
|
||||
if !scope.contains("urn:matrix:org.matrix.msc2967.client:device:") {
|
||||
return Err(err!(Request(Unknown("the scope does not include a device ID"))));
|
||||
}
|
||||
if query.code_challenge_method != "S256" {
|
||||
|
@ -48,22 +50,23 @@ pub(crate) async fn authorize(
|
|||
| None => {
|
||||
return Ok(oidc_login_form(hostname, &query));
|
||||
},
|
||||
| Some(token) => if services.users.find_from_token(token).await.is_err() {
|
||||
return Ok(oidc_login_form(hostname, &query));
|
||||
}
|
||||
| Some(token) =>
|
||||
if services.users.find_from_token(token).await.is_err() {
|
||||
return Ok(oidc_login_form(hostname, &query));
|
||||
},
|
||||
}
|
||||
// TODO register the device ID ?
|
||||
|
||||
services
|
||||
.oidc
|
||||
.endpoint()
|
||||
.with_solicitor(oidc_consent_form(hostname, &query))
|
||||
.with_solicitor(oidc_consent_form(hostname, &query))
|
||||
.authorization_flow()
|
||||
.execute(oauth)
|
||||
.map_err(|err| err!("authorization failed: {err:?}"))
|
||||
}
|
||||
|
||||
/// Wether a user allows their device to access this homeserver's resources.
|
||||
/// Whether a user allows their device to access this homeserver's resources.
|
||||
#[derive(serde::Deserialize)]
|
||||
pub(crate) struct Allowance {
|
||||
allow: Option<bool>,
|
||||
|
@ -85,14 +88,12 @@ pub(crate) async fn authorize_consent(
|
|||
services
|
||||
.oidc
|
||||
.endpoint()
|
||||
.with_solicitor(
|
||||
FnSolicitor(move |_: &mut _, solicitation: Solicitation<'_>|
|
||||
match allowed {
|
||||
| false => OwnerConsent::Denied,
|
||||
| true => OwnerConsent::Authorized(solicitation.pre_grant().client_id.clone())
|
||||
}
|
||||
)
|
||||
)
|
||||
.with_solicitor(FnSolicitor(
|
||||
move |_: &mut _, solicitation: Solicitation<'_>| match allowed {
|
||||
| false => OwnerConsent::Denied,
|
||||
| true => OwnerConsent::Authorized(solicitation.pre_grant().client_id.clone()),
|
||||
},
|
||||
))
|
||||
.authorization_flow()
|
||||
.execute(oauth)
|
||||
.map_err(|err| err!(Request(Unknown("consent request failed: {err:?}"))))
|
||||
|
|
|
@ -3,71 +3,76 @@
|
|||
/// [MSC2965]: https://github.com/matrix-org/matrix-spec-proposals/pull/2965
|
||||
use axum::extract::State;
|
||||
use conduwuit::Result;
|
||||
use ruma::serde::Raw;
|
||||
use ruma::api::client::{
|
||||
error::{
|
||||
Error as ClientError,
|
||||
ErrorKind as ClientErrorKind,
|
||||
ErrorBody as ClientErrorBody,
|
||||
},
|
||||
discovery::get_authorization_server_metadata::{
|
||||
self,
|
||||
msc2965::{
|
||||
AccountManagementAction,
|
||||
AuthorizationServerMetadata,
|
||||
CodeChallengeMethod,
|
||||
GrantType,
|
||||
Prompt,
|
||||
ResponseMode,
|
||||
ResponseType,
|
||||
Response,
|
||||
use ruma::{
|
||||
api::client::{
|
||||
discovery::get_authorization_server_metadata::{
|
||||
self,
|
||||
msc2965::{
|
||||
AccountManagementAction, AuthorizationServerMetadata, CodeChallengeMethod,
|
||||
GrantType, Prompt, Response, ResponseMode, ResponseType,
|
||||
},
|
||||
},
|
||||
error::{
|
||||
Error as ClientError, ErrorBody as ClientErrorBody, ErrorKind as ClientErrorKind,
|
||||
},
|
||||
},
|
||||
serde::Raw,
|
||||
};
|
||||
use crate::{conduwuit::Error, Ruma, RumaResponse};
|
||||
|
||||
use crate::{Ruma, RumaResponse, conduwuit::Error};
|
||||
|
||||
/// # `GET /_matrix/client/unstable/org.matrix.msc2965/auth_metadata`
|
||||
///
|
||||
/// If `globals.auth.enable_oidc_login` is set, advertise this homeserver's OAuth2 endpoints.
|
||||
/// Otherwise, MSC2965 requires that the homeserver responds with 404/M_UNRECOGNIZED.
|
||||
/// If `globals.auth.enable_oidc_login` is set, advertise this homeserver's
|
||||
/// OAuth2 endpoints. Otherwise, MSC2965 requires that the homeserver responds
|
||||
/// with 404/M_UNRECOGNIZED.
|
||||
pub(crate) async fn get_auth_metadata(
|
||||
State(services): State<crate::State>,
|
||||
_body: Ruma<get_authorization_server_metadata::msc2965::Request>,
|
||||
) -> Result<RumaResponse<Response>> {
|
||||
let unrecognized_error = Err(Error::Ruma(
|
||||
ClientError::new(
|
||||
http::StatusCode::NOT_FOUND,
|
||||
ClientErrorBody::Standard {
|
||||
kind: ClientErrorKind::Unrecognized,
|
||||
message: "This homeserver doesn't do OIDC authentication.".to_string()
|
||||
}
|
||||
)
|
||||
));
|
||||
let unrecognized_error = Err(Error::Ruma(ClientError::new(
|
||||
http::StatusCode::NOT_FOUND,
|
||||
ClientErrorBody::Standard {
|
||||
kind: ClientErrorKind::Unrecognized,
|
||||
message: "This homeserver doesn't do OIDC authentication.".to_owned(),
|
||||
},
|
||||
)));
|
||||
let Some(ref auth) = services.server.config.auth else {
|
||||
return unrecognized_error;
|
||||
};
|
||||
if ! auth.enable_oidc_login {
|
||||
if !auth.enable_oidc_login {
|
||||
return unrecognized_error;
|
||||
};
|
||||
}
|
||||
// Advertise this homeserver's access URL as the issuer URL.
|
||||
// Unwrap all Url::parse() calls because the issuer URL is validated at startup.
|
||||
let issuer = services.server.config.well_known.client.as_ref().unwrap();
|
||||
let account_management_uri = auth
|
||||
.enable_oidc_account_management
|
||||
.then_some(issuer.join("/_matrix/client/unstable/org.matrix.msc2964/account").unwrap());
|
||||
let account_management_uri = auth.enable_oidc_account_management.then_some(
|
||||
issuer
|
||||
.join("/_matrix/client/unstable/org.matrix.msc2964/account")
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let metadata = AuthorizationServerMetadata {
|
||||
issuer: issuer.clone(),
|
||||
authorization_endpoint:
|
||||
issuer.join("/_matrix/client/unstable/org.matrix.msc2964/authorize").unwrap(),
|
||||
device_authorization_endpoint:
|
||||
Some(issuer.join("/_matrix/client/unstable/org.matrix.msc2964/device").unwrap()),
|
||||
token_endpoint:
|
||||
issuer.join("/_matrix/client/unstable/org.matrix.msc2964/token").unwrap(),
|
||||
registration_endpoint:
|
||||
Some(issuer.join("/_matrix/client/unstable/org.matrix.msc2964/device/register").unwrap()),
|
||||
revocation_endpoint:
|
||||
issuer.join("_matrix/client/unstable/org.matrix.msc2964/revoke").unwrap(),
|
||||
authorization_endpoint: issuer
|
||||
.join("/_matrix/client/unstable/org.matrix.msc2964/authorize")
|
||||
.unwrap(),
|
||||
device_authorization_endpoint: Some(
|
||||
issuer
|
||||
.join("/_matrix/client/unstable/org.matrix.msc2964/device")
|
||||
.unwrap(),
|
||||
),
|
||||
token_endpoint: issuer
|
||||
.join("/_matrix/client/unstable/org.matrix.msc2964/token")
|
||||
.unwrap(),
|
||||
registration_endpoint: Some(
|
||||
issuer
|
||||
.join("/_matrix/client/unstable/org.matrix.msc2964/device/register")
|
||||
.unwrap(),
|
||||
),
|
||||
revocation_endpoint: issuer
|
||||
.join("_matrix/client/unstable/org.matrix.msc2964/revoke")
|
||||
.unwrap(),
|
||||
response_types_supported: [ResponseType::Code].into(),
|
||||
grant_types_supported: [GrantType::AuthorizationCode, GrantType::RefreshToken].into(),
|
||||
response_modes_supported: [ResponseMode::Fragment, ResponseMode::Query].into(),
|
||||
|
@ -77,11 +82,12 @@ pub(crate) async fn get_auth_metadata(
|
|||
AccountManagementAction::Profile,
|
||||
AccountManagementAction::SessionView,
|
||||
AccountManagementAction::SessionEnd,
|
||||
].into(),
|
||||
]
|
||||
.into(),
|
||||
prompt_values_supported: match services.server.config.allow_registration {
|
||||
| true => vec![Prompt::Create],
|
||||
| false => vec![]
|
||||
}
|
||||
| false => vec![],
|
||||
},
|
||||
};
|
||||
let metadata = Raw::new(&metadata).expect("authorization server metadata should serialize");
|
||||
|
||||
|
|
|
@ -1,16 +1,7 @@
|
|||
use conduwuit_web::oidc::{
|
||||
oidc_consent_form,
|
||||
AuthorizationQuery,
|
||||
LoginError,
|
||||
LoginQuery,
|
||||
OidcRequest,
|
||||
OidcResponse,
|
||||
};
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Result,
|
||||
err,
|
||||
utils::hash::verify_password,
|
||||
use conduwuit::{Result, err, utils::hash::verify_password};
|
||||
use conduwuit_web::oidc::{
|
||||
AuthorizationQuery, LoginError, LoginQuery, OidcRequest, OidcResponse, oidc_consent_form,
|
||||
};
|
||||
use ruma::user_id::UserId;
|
||||
|
||||
|
@ -25,17 +16,15 @@ pub(crate) async fn oidc_login(
|
|||
State(services): State<crate::State>,
|
||||
request: OidcRequest,
|
||||
) -> Result<OidcResponse> {
|
||||
let query: LoginQuery = request.clone().try_into().map_err(|LoginError(err)|
|
||||
let query: LoginQuery = request.clone().try_into().map_err(|LoginError(err)| {
|
||||
err!(Request(InvalidParam("Cannot process login form. {err}")))
|
||||
)?;
|
||||
})?;
|
||||
// Only accept local usernames. Mostly to simplify things at first.
|
||||
let user_id = UserId::parse_with_server_name(
|
||||
query.username.clone(),
|
||||
&services.config.server_name
|
||||
)
|
||||
.map_err(|e| err!(Request(InvalidUsername("Username is invalid: {e}"))))?;
|
||||
let user_id =
|
||||
UserId::parse_with_server_name(query.username.clone(), &services.config.server_name)
|
||||
.map_err(|e| err!(Request(InvalidUsername("Username is invalid: {e}"))))?;
|
||||
|
||||
if ! services.users.exists(&user_id).await {
|
||||
if !services.users.exists(&user_id).await {
|
||||
return Err(err!(Request(Unknown("unknown username"))));
|
||||
}
|
||||
tracing::info!("logging in: {user_id:?}");
|
||||
|
@ -48,7 +37,7 @@ pub(crate) async fn oidc_login(
|
|||
if valid_hash.is_empty() {
|
||||
return Err(err!(Request(UserDeactivated("the user's hash was not found"))));
|
||||
}
|
||||
if let Err(_) = verify_password(&query.password, &valid_hash) {
|
||||
if verify_password(&query.password, &valid_hash).is_err() {
|
||||
return Err(err!(Request(InvalidParam("password does not match"))));
|
||||
}
|
||||
tracing::info!("{user_id:?} passed, forwarding to consent page");
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
/// OIDC
|
||||
///
|
||||
/// Stands for OpenID Connect, and is an authentication scheme relying on OAuth2.
|
||||
/// The [MSC2964] Matrix Spec Proposal describes an authentication process based
|
||||
/// on the OIDC flow, with restrictions. See the [sample flow] for details on
|
||||
/// what's expected.
|
||||
///
|
||||
/// This module implements the needed endpoints. It relies on the [oxide-auth]
|
||||
/// crate, and the [`service::oidc`] and [`web::oidc`] modules.
|
||||
///
|
||||
/// [MSC2964]: https://github.com/matrix-org/matrix-spec-proposals/pull/2964
|
||||
/// [oxide-auth]: https://docs.rs/oxide-auth
|
||||
/// [sample flow]: https://github.com/sandhose/matrix-spec-proposals/blob/msc/sandhose/oauth2-profile/proposals/2964-oauth2-profile.md#sample-flow
|
||||
//! OIDC
|
||||
//!
|
||||
//! Stands for OpenID Connect, and is an authentication scheme relying on
|
||||
//! OAuth2. The [MSC2964] Matrix Spec Proposal describes an authentication
|
||||
//! process based on the OIDC flow, with restrictions. See the [sample flow] for
|
||||
//! details on what's expected.
|
||||
//!
|
||||
//! This module implements the needed endpoints. It relies on the [oxide-auth]
|
||||
//! crate, and the [`service::oidc`] and [`web::oidc`] modules.
|
||||
//!
|
||||
//! [MSC2964]: https://github.com/matrix-org/matrix-spec-proposals/pull/2964
|
||||
//! [oxide-auth]: https://docs.rs/oxide-auth
|
||||
//! [sample flow]: https://github.com/sandhose/matrix-spec-proposals/blob/msc/sandhose/oauth2-profile/proposals/2964-oauth2-profile.md#sample-flow
|
||||
|
||||
mod authorize;
|
||||
mod discovery;
|
||||
mod login;
|
||||
mod authorize;
|
||||
mod token;
|
||||
mod register;
|
||||
mod token;
|
||||
|
||||
pub(crate) use self::discovery::get_auth_metadata;
|
||||
pub(crate) use self::login::oidc_login;
|
||||
pub(crate) use self::authorize::{authorize, authorize_consent};
|
||||
pub(crate) use self::register::register_client;
|
||||
pub(crate) use self::token::token;
|
||||
pub(crate) use self::{
|
||||
authorize::{authorize, authorize_consent},
|
||||
discovery::get_auth_metadata,
|
||||
login::oidc_login,
|
||||
register::register_client,
|
||||
token::token,
|
||||
};
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
use oxide_auth::primitives::prelude::Client;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
};
|
||||
use axum::{Json, extract::State};
|
||||
use conduwuit::{Result, err};
|
||||
use ruma::DeviceId;
|
||||
use oxide_auth::primitives::prelude::Client;
|
||||
use reqwest::Url;
|
||||
use ruma::DeviceId;
|
||||
|
||||
/// The required parameters to register a new client for OAuth2 application.
|
||||
#[derive(serde::Deserialize, Clone)]
|
||||
pub(crate) struct ClientQuery {
|
||||
/// Human-readable name.
|
||||
client_name: String,
|
||||
/// A public page that tells more about the client. All other links must be within.
|
||||
client_uri: Url,
|
||||
client_name: String,
|
||||
/// A public page that tells more about the client. All other links must be
|
||||
/// within.
|
||||
client_uri: Url,
|
||||
/// Redirect URIs declared by the client. At least one.
|
||||
redirect_uris: Vec<Url>,
|
||||
/// Must be ["code"].
|
||||
/// Must be `["code"]`.
|
||||
response_types: Vec<String>,
|
||||
/// Must include "authorization_type" and "refresh_token".
|
||||
grant_types: Vec<String>,
|
||||
|
@ -51,8 +49,9 @@ pub(crate) struct ClientResponse {
|
|||
|
||||
/// # `GET /_matrix/client/unstable/org.matrix.msc2964/device/register`
|
||||
///
|
||||
/// Register a client, as specified in [MSC2966]. This client, "device" in OIDC parlance,
|
||||
/// will have the right to submit [super::authorize::authorize] requests.
|
||||
/// Register a client, as specified in [MSC2966]. This client, "device" in OIDC
|
||||
/// parlance, will have the right to submit [super::authorize::authorize]
|
||||
/// requests.
|
||||
///
|
||||
/// [MSC2966]: https://github.com/matrix-org/matrix-spec-proposals/pull/2966
|
||||
pub(crate) async fn register_client(
|
||||
|
@ -66,15 +65,17 @@ pub(crate) async fn register_client(
|
|||
};
|
||||
let device_id = DeviceId::new();
|
||||
let scope = format!(
|
||||
"urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:{}",
|
||||
device_id
|
||||
"urn:matrix:org.matrix.msc2967.client:api:* \
|
||||
urn:matrix:org.matrix.msc2967.client:device:{device_id}"
|
||||
);
|
||||
// TODO check if the users service needs an update.
|
||||
//services.users.update_device_metadata();
|
||||
services.oidc.register_client(&Client::public(
|
||||
&device_id.to_string(),
|
||||
device_id.as_ref(),
|
||||
redirect_uri.into(),
|
||||
scope.parse().expect("device ID should parse in Matrix scope"),
|
||||
scope
|
||||
.parse()
|
||||
.expect("device ID should parse in Matrix scope"),
|
||||
))?;
|
||||
|
||||
Ok(Json(ClientResponse {
|
||||
|
@ -88,6 +89,6 @@ pub(crate) async fn register_client(
|
|||
token_endpoint_auth_method: client.token_endpoint_auth_method.clone(),
|
||||
response_types: client.response_types.clone(),
|
||||
grant_types: client.grant_types.clone(),
|
||||
application_type: client.application_type.clone(),
|
||||
application_type: client.application_type,
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use conduwuit_web::oidc::{OidcRequest, OidcResponse};
|
||||
use conduwuit::{Result, err};
|
||||
use oxide_auth::endpoint::QueryParameter;
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Result, err};
|
||||
use conduwuit_web::oidc::{OidcRequest, OidcResponse};
|
||||
use oxide_auth::endpoint::QueryParameter;
|
||||
|
||||
/// # `POST /_matrix/client/unstable/org.matrix.msc2964/token`
|
||||
///
|
||||
|
@ -20,17 +20,14 @@ pub(crate) async fn token(
|
|||
let endpoint = services.oidc.endpoint();
|
||||
|
||||
match grant_type.as_deref() {
|
||||
| Some("authorization_code") =>
|
||||
endpoint
|
||||
.access_token_flow()
|
||||
.execute(oauth)
|
||||
.map_err(|err| err!(Request(Unknown("token grant failed: {err:?}")))),
|
||||
| Some("refresh_token") =>
|
||||
endpoint
|
||||
.refresh_flow()
|
||||
.execute(oauth)
|
||||
.map_err(|err| err!(Request(Unknown("token refresh failed: {err:?}")))),
|
||||
| other =>
|
||||
Err(err!(Request(Unknown("unsupported grant type: {other:?}")))),
|
||||
| Some("authorization_code") => endpoint
|
||||
.access_token_flow()
|
||||
.execute(oauth)
|
||||
.map_err(|err| err!(Request(Unknown("token grant failed: {err:?}")))),
|
||||
| Some("refresh_token") => endpoint
|
||||
.refresh_flow()
|
||||
.execute(oauth)
|
||||
.map_err(|err| err!(Request(Unknown("token refresh failed: {err:?}")))),
|
||||
| other => Err(err!(Request(Unknown("unsupported grant type: {other:?}")))),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,7 @@ use futures::StreamExt;
|
|||
use ruma::api::client::{
|
||||
discovery::{
|
||||
discover_homeserver::{
|
||||
self,
|
||||
HomeserverInfo,
|
||||
SlidingSyncProxyInfo,
|
||||
AuthenticationServerInfo,
|
||||
self, AuthenticationServerInfo, HomeserverInfo, SlidingSyncProxyInfo,
|
||||
},
|
||||
discover_support::{self, Contact},
|
||||
},
|
||||
|
@ -33,16 +30,14 @@ pub(crate) async fn well_known_client(
|
|||
identity_server: None,
|
||||
sliding_sync_proxy: Some(SlidingSyncProxyInfo { url: client_url.clone() }),
|
||||
tile_server: None,
|
||||
authentication: services.config.auth.as_ref().and_then(|auth|
|
||||
auth.enable_oidc_login.then_some(
|
||||
AuthenticationServerInfo::new(
|
||||
authentication: services.config.auth.as_ref().and_then(|auth| {
|
||||
auth.enable_oidc_login
|
||||
.then_some(AuthenticationServerInfo::new(
|
||||
client_url.clone(),
|
||||
auth.enable_oidc_account_management.then_some(
|
||||
format!("{client_url}/account")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
auth.enable_oidc_account_management
|
||||
.then_some(format!("{client_url}/account")),
|
||||
))
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue