mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-06-27 00:56:36 +02:00
impl MSC2965: self-advertise as OIDC authentication provider
MSC2965 proposes to let the homeserver advertise its current OIDC authentication issuer. These changes let conduwuit advertise itself as the issuer when [global.auth.enable_oidc_login] is set. It also advertises its account management endpoint if [global.auth.enable_oidc_account_management] is set. None of these endpoints are implemented. This commit only implements the bare advertisement, as requested by the MSC.
This commit is contained in:
parent
dcbc4b54c5
commit
f8c7b2ae3a
9 changed files with 157 additions and 3 deletions
|
@ -368,6 +368,7 @@ features = [
|
||||||
"unstable-msc2666",
|
"unstable-msc2666",
|
||||||
"unstable-msc2867",
|
"unstable-msc2867",
|
||||||
"unstable-msc2870",
|
"unstable-msc2870",
|
||||||
|
"unstable-msc2965",
|
||||||
"unstable-msc3026",
|
"unstable-msc3026",
|
||||||
"unstable-msc3061",
|
"unstable-msc3061",
|
||||||
"unstable-msc3245",
|
"unstable-msc3245",
|
||||||
|
|
|
@ -1624,6 +1624,20 @@
|
||||||
#
|
#
|
||||||
#dual_protocol = false
|
#dual_protocol = false
|
||||||
|
|
||||||
|
[global.auth]
|
||||||
|
|
||||||
|
# Use this homeserver as the OIDC authentication reference.
|
||||||
|
# Note that the legacy Matrix authentication still will work.
|
||||||
|
# Unset by default.
|
||||||
|
#
|
||||||
|
#enable_oidc_login =
|
||||||
|
|
||||||
|
# The URL where the user is able to access the account management
|
||||||
|
# capabilities of the homeserver. Only used if `enable_oidc_login` is set.
|
||||||
|
# Unset by default.
|
||||||
|
#
|
||||||
|
#enable_oidc_account_management =
|
||||||
|
|
||||||
[global.well_known]
|
[global.well_known]
|
||||||
|
|
||||||
# The server URL that the client well-known file will serve. This should
|
# The server URL that the client well-known file will serve. This should
|
||||||
|
|
|
@ -2,6 +2,7 @@ pub(super) mod account;
|
||||||
pub(super) mod account_data;
|
pub(super) mod account_data;
|
||||||
pub(super) mod alias;
|
pub(super) mod alias;
|
||||||
pub(super) mod appservice;
|
pub(super) mod appservice;
|
||||||
|
pub(super) mod oidc;
|
||||||
pub(super) mod backup;
|
pub(super) mod backup;
|
||||||
pub(super) mod capabilities;
|
pub(super) mod capabilities;
|
||||||
pub(super) mod context;
|
pub(super) mod context;
|
||||||
|
@ -44,6 +45,7 @@ pub(super) use account::*;
|
||||||
pub(super) use account_data::*;
|
pub(super) use account_data::*;
|
||||||
pub(super) use alias::*;
|
pub(super) use alias::*;
|
||||||
pub(super) use appservice::*;
|
pub(super) use appservice::*;
|
||||||
|
pub(super) use oidc::*;
|
||||||
pub(super) use backup::*;
|
pub(super) use backup::*;
|
||||||
pub(super) use capabilities::*;
|
pub(super) use capabilities::*;
|
||||||
pub(super) use context::*;
|
pub(super) use context::*;
|
||||||
|
|
89
src/api/client/oidc/discovery.rs
Normal file
89
src/api/client/oidc/discovery.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/// Manual implementation of [MSC2965]'s OIDC server discovery.
|
||||||
|
///
|
||||||
|
/// [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 crate::{conduwuit::Error, Ruma, RumaResponse};
|
||||||
|
|
||||||
|
/// # `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.
|
||||||
|
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 Some(ref auth) = services.server.config.auth else {
|
||||||
|
return unrecognized_error;
|
||||||
|
};
|
||||||
|
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 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(),
|
||||||
|
response_types_supported: [ResponseType::Code].into(),
|
||||||
|
grant_types_supported: [GrantType::AuthorizationCode, GrantType::RefreshToken].into(),
|
||||||
|
response_modes_supported: [ResponseMode::Fragment, ResponseMode::Query].into(),
|
||||||
|
code_challenge_methods_supported: [CodeChallengeMethod::S256].into(),
|
||||||
|
account_management_uri,
|
||||||
|
account_management_actions_supported: [
|
||||||
|
AccountManagementAction::Profile,
|
||||||
|
AccountManagementAction::SessionView,
|
||||||
|
AccountManagementAction::SessionEnd,
|
||||||
|
].into(),
|
||||||
|
prompt_values_supported: match services.server.config.allow_registration {
|
||||||
|
| true => vec![Prompt::Create],
|
||||||
|
| false => vec![]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let metadata = Raw::new(&metadata).expect("authorization server metadata should serialize");
|
||||||
|
|
||||||
|
Ok(RumaResponse(Response::new(metadata)))
|
||||||
|
}
|
3
src/api/client/oidc/mod.rs
Normal file
3
src/api/client/oidc/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
mod discovery;
|
||||||
|
|
||||||
|
pub(crate) use self::discovery::get_auth_metadata;
|
|
@ -2,7 +2,12 @@ use axum::{Json, extract::State, response::IntoResponse};
|
||||||
use conduwuit::{Error, Result};
|
use conduwuit::{Error, Result};
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
discovery::{
|
discovery::{
|
||||||
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
|
discover_homeserver::{
|
||||||
|
self,
|
||||||
|
HomeserverInfo,
|
||||||
|
SlidingSyncProxyInfo,
|
||||||
|
AuthenticationServerInfo,
|
||||||
|
},
|
||||||
discover_support::{self, Contact},
|
discover_support::{self, Contact},
|
||||||
},
|
},
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
|
@ -25,8 +30,18 @@ pub(crate) async fn well_known_client(
|
||||||
Ok(discover_homeserver::Response {
|
Ok(discover_homeserver::Response {
|
||||||
homeserver: HomeserverInfo { base_url: client_url.clone() },
|
homeserver: HomeserverInfo { base_url: client_url.clone() },
|
||||||
identity_server: None,
|
identity_server: None,
|
||||||
sliding_sync_proxy: Some(SlidingSyncProxyInfo { url: client_url }),
|
sliding_sync_proxy: Some(SlidingSyncProxyInfo { url: client_url.clone() }),
|
||||||
tile_server: None,
|
tile_server: None,
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,9 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||||
.ruma_route(&client::get_protocols_route)
|
.ruma_route(&client::get_protocols_route)
|
||||||
.route("/_matrix/client/unstable/thirdparty/protocols",
|
.route("/_matrix/client/unstable/thirdparty/protocols",
|
||||||
get(client::get_protocols_route_unstable))
|
get(client::get_protocols_route_unstable))
|
||||||
|
// MSC2965 is still not stabilized. See https://github.com/sandhose/matrix-spec-proposals/blob/msc/sandhose/oidc-discovery/proposals/2965-auth-metadata.md#unstable-prefix
|
||||||
|
.route("/_matrix/client/unstable/org.matrix.msc2965/auth_metadata",
|
||||||
|
get(client::get_auth_metadata))
|
||||||
.ruma_route(&client::send_message_event_route)
|
.ruma_route(&client::send_message_event_route)
|
||||||
.ruma_route(&client::send_state_event_for_key_route)
|
.ruma_route(&client::send_state_event_for_key_route)
|
||||||
.ruma_route(&client::get_state_events_route)
|
.ruma_route(&client::get_state_events_route)
|
||||||
|
|
|
@ -271,6 +271,17 @@ pub fn check(config: &Config) -> Result {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(auth) = &config.auth {
|
||||||
|
if auth.enable_oidc_login {
|
||||||
|
if config.well_known.client.is_none() {
|
||||||
|
return Err!(Config(
|
||||||
|
"auth.enable_oidc_login",
|
||||||
|
"Oidc authentication is enabled but the well-known client is not set."
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,9 @@ pub struct Config {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tls: TlsConfig,
|
pub tls: TlsConfig,
|
||||||
|
|
||||||
/// The UNIX socket continuwuity will listen on.
|
pub auth: Option<AuthConfig>,
|
||||||
|
|
||||||
|
/// The UNIX socket conduwuit will listen on.
|
||||||
///
|
///
|
||||||
/// continuwuity cannot listen on both an IP address and a UNIX socket. If
|
/// continuwuity cannot listen on both an IP address and a UNIX socket. If
|
||||||
/// listening on a UNIX socket, you MUST remove/comment the `address` key.
|
/// listening on a UNIX socket, you MUST remove/comment the `address` key.
|
||||||
|
@ -1880,6 +1882,20 @@ pub struct TlsConfig {
|
||||||
pub dual_protocol: bool,
|
pub dual_protocol: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Default)]
|
||||||
|
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.auth")]
|
||||||
|
pub struct AuthConfig {
|
||||||
|
/// Use this homeserver as the OIDC authentication reference.
|
||||||
|
/// Note that the legacy Matrix authentication still will work.
|
||||||
|
/// Unset by default.
|
||||||
|
pub enable_oidc_login: bool,
|
||||||
|
|
||||||
|
/// The URL where the user is able to access the account management
|
||||||
|
/// capabilities of the homeserver. Only used if `enable_oidc_login` is set.
|
||||||
|
/// Unset by default.
|
||||||
|
pub enable_oidc_account_management: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(rustdoc::broken_intra_doc_links, rustdoc::bare_urls)]
|
#[allow(rustdoc::broken_intra_doc_links, rustdoc::bare_urls)]
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default)]
|
||||||
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.well_known")]
|
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.well_known")]
|
||||||
|
|
Loading…
Add table
Reference in a new issue