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",
"state-res",
"unstable-msc2448",
"unstable-msc2965",
"unstable-msc3575",
]
git = "https://github.com/ruma/ruma.git"

View file

@ -13,6 +13,7 @@ mod media;
mod membership;
mod message;
mod openid;
mod oidc;
mod presence;
mod profile;
mod push;
@ -73,6 +74,7 @@ pub use unversioned::*;
pub use user_directory::*;
pub use voip::*;
pub use well_known::*;
pub use oidc::*;
pub const DEVICE_ID_LENGTH: usize = 10;
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::{
self, HomeserverInfo, SlidingSyncProxyInfo,
self,
AuthenticationServerInfo,
HomeserverInfo,
SlidingSyncProxyInfo,
};
use crate::{services, Result, Ruma};
@ -11,12 +14,21 @@ pub async fn well_known_client(
_body: Ruma<discover_homeserver::Request>,
) -> Result<discover_homeserver::Response> {
let client_url = services().globals.well_known_client();
let authentication = &services().globals.config.authentication;
Ok(discover_homeserver::Response {
homeserver: HomeserverInfo {
base_url: client_url.clone(),
},
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,
#[serde(default)]
pub proxy: ProxyConfig,
#[serde(default)]
pub authentication: AuthenticationConfig,
pub jwt_secret: Option<String>,
#[serde(default = "default_trusted_servers")]
pub trusted_servers: Vec<OwnedServerName>,
@ -115,6 +117,14 @@ pub struct WellKnownConfig {
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] = &[
"cache_capacity",
"turn_username",
@ -260,6 +270,20 @@ impl fmt::Display for Config {
}),
("Well-known server name", well_known_server.as_str()),
("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();

View file

@ -426,6 +426,14 @@ fn routes(config: &Config) -> Router {
.ruma_route(client_server::get_relating_events_route)
.ruma_route(client_server::get_hierarchy_route)
.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(
"/_matrix/client/r0/rooms/:room_id/initialSync",
get(initial_sync),