1
0
Fork 0
mirror of https://gitlab.com/famedly/conduit.git synced 2025-06-27 16:35:59 +00:00

impl MSC2965: self-advertise as OIDC authentication provider

This commit is contained in:
lafleur 2025-04-17 17:59:29 +02:00
parent a7e6f60b41
commit c322cbcb79
6 changed files with 125 additions and 2 deletions

View file

@ -161,6 +161,7 @@ features = [
"ring-compat", "ring-compat",
"state-res", "state-res",
"unstable-msc2448", "unstable-msc2448",
"unstable-msc2965",
"unstable-msc3575", "unstable-msc3575",
] ]
git = "https://github.com/ruma/ruma.git" git = "https://github.com/ruma/ruma.git"

View file

@ -13,6 +13,7 @@ mod media;
mod membership; mod membership;
mod message; mod message;
mod openid; mod openid;
mod oidc;
mod presence; mod presence;
mod profile; mod profile;
mod push; mod push;
@ -73,6 +74,7 @@ pub use unversioned::*;
pub use user_directory::*; pub use user_directory::*;
pub use voip::*; pub use voip::*;
pub use well_known::*; pub use well_known::*;
pub use oidc::*;
pub const DEVICE_ID_LENGTH: usize = 10; pub const DEVICE_ID_LENGTH: usize = 10;
pub const TOKEN_LENGTH: usize = 32; pub const TOKEN_LENGTH: usize = 32;

View file

@ -0,0 +1,76 @@
/// Manual implementation of [MSC2965]'s OIDC server discovery.
///
/// [MSC2965]: https://github.com/matrix-org/matrix-spec-proposals/pull/2965
use crate::{services, Error, Result, Ruma, RumaResponse};
use ruma::serde::Raw;
use ruma::api::client::{
error::ErrorKind as ClientErrorKind,
discovery::get_authorization_server_metadata::{
self,
msc2965::{
AccountManagementAction,
AuthorizationServerMetadata,
CodeChallengeMethod,
GrantType,
Prompt,
ResponseMode,
ResponseType,
Response,
},
},
};
/// # `GET /_matrix/client/unstable/org.matrix.msc2965/auth_metadata`
/// # `GET /_matrix/client/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 async fn get_auth_metadata(
_body: Ruma<get_authorization_server_metadata::msc2965::Request>,
) -> Result<RumaResponse<Response>> {
let authentication = &services().globals.config.authentication;
if ! authentication.enable_oidc_login {
return Err(Error::BadRequest(
ClientErrorKind::Unknown,
"This server doesn't do OIDC authentication."
));
};
// 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().globals.config.well_known.client.as_ref().unwrap();
let account_management_uri = authentication
.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().globals.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)))
}

View file

@ -1,5 +1,8 @@
use ruma::api::client::discovery::discover_homeserver::{ use ruma::api::client::discovery::discover_homeserver::{
self, HomeserverInfo, SlidingSyncProxyInfo, self,
AuthenticationServerInfo,
HomeserverInfo,
SlidingSyncProxyInfo,
}; };
use crate::{services, Result, Ruma}; use crate::{services, Result, Ruma};
@ -11,12 +14,21 @@ pub async fn well_known_client(
_body: Ruma<discover_homeserver::Request>, _body: Ruma<discover_homeserver::Request>,
) -> Result<discover_homeserver::Response> { ) -> Result<discover_homeserver::Response> {
let client_url = services().globals.well_known_client(); let client_url = services().globals.well_known_client();
let authentication = &services().globals.config.authentication;
Ok(discover_homeserver::Response { Ok(discover_homeserver::Response {
homeserver: HomeserverInfo { homeserver: HomeserverInfo {
base_url: client_url.clone(), 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() }),
authentication: authentication.enable_oidc_login.then_some(
AuthenticationServerInfo::new(
client_url.clone(),
authentication.enable_oidc_account_management.then_some(
format!("{client_url}/account")
),
)
)
}) })
} }

View file

@ -67,6 +67,8 @@ pub struct Config {
pub tracing_flame: bool, pub tracing_flame: bool,
#[serde(default)] #[serde(default)]
pub proxy: ProxyConfig, pub proxy: ProxyConfig,
#[serde(default)]
pub authentication: AuthenticationConfig,
pub jwt_secret: Option<String>, pub jwt_secret: Option<String>,
#[serde(default = "default_trusted_servers")] #[serde(default = "default_trusted_servers")]
pub trusted_servers: Vec<OwnedServerName>, pub trusted_servers: Vec<OwnedServerName>,
@ -115,6 +117,14 @@ pub struct WellKnownConfig {
pub server: Option<OwnedServerName>, pub server: Option<OwnedServerName>,
} }
#[derive(Clone, Debug, Deserialize, Default)]
pub struct AuthenticationConfig {
#[serde(default = "false_fn")]
pub enable_oidc_login: bool,
#[serde(default = "false_fn")]
pub enable_oidc_account_management: bool,
}
const DEPRECATED_KEYS: &[&str] = &[ const DEPRECATED_KEYS: &[&str] = &[
"cache_capacity", "cache_capacity",
"turn_username", "turn_username",
@ -260,6 +270,20 @@ impl fmt::Display for Config {
}), }),
("Well-known server name", well_known_server.as_str()), ("Well-known server name", well_known_server.as_str()),
("Well-known client URL", &self.well_known_client()), ("Well-known client URL", &self.well_known_client()),
("OIDC authentication",
if self.authentication.enable_oidc_login {
"enabled"
} else {
"disabled"
}
),
("OIDC account management enabled",
if self.authentication.enable_oidc_account_management {
"enabled"
} else {
"disabled"
}
),
]; ];
let mut msg: String = "Active config values:\n\n".to_owned(); let mut msg: String = "Active config values:\n\n".to_owned();

View file

@ -426,6 +426,14 @@ fn routes(config: &Config) -> Router {
.ruma_route(client_server::get_relating_events_route) .ruma_route(client_server::get_relating_events_route)
.ruma_route(client_server::get_hierarchy_route) .ruma_route(client_server::get_hierarchy_route)
.ruma_route(client_server::well_known_client) .ruma_route(client_server::well_known_client)
.route(
"/_matrix/client/unstable/org.matrix.msc2965/auth_metadata",
get(client_server::get_auth_metadata),
)
.route(
"/_matrix/client/auth_metadata",
get(client_server::get_auth_metadata),
)
.route( .route(
"/_matrix/client/r0/rooms/:room_id/initialSync", "/_matrix/client/r0/rooms/:room_id/initialSync",
get(initial_sync), get(initial_sync),