From 57c702d732c230dea809d273aeac40ca6e371a10 Mon Sep 17 00:00:00 2001 From: lafleur Date: Thu, 17 Apr 2025 20:24:13 +0200 Subject: [PATCH] impl MSC2966: dynamically register Oidc clients --- src/api/client_server/oidc/mod.rs | 2 + src/api/client_server/oidc/register.rs | 93 ++++++++++++++++++++++++++ src/main.rs | 3 + src/service/oidc/mod.rs | 10 +++ 4 files changed, 108 insertions(+) create mode 100644 src/api/client_server/oidc/register.rs diff --git a/src/api/client_server/oidc/mod.rs b/src/api/client_server/oidc/mod.rs index 5be3dba8..50fd403e 100644 --- a/src/api/client_server/oidc/mod.rs +++ b/src/api/client_server/oidc/mod.rs @@ -1,9 +1,11 @@ mod discovery; mod authorize; mod login; +mod register; mod token; pub use discovery::get_auth_metadata; pub use authorize::{authorize, authorize_consent}; pub use login::oidc_login; +pub use register::register_client; pub use token::token; diff --git a/src/api/client_server/oidc/register.rs b/src/api/client_server/oidc/register.rs new file mode 100644 index 00000000..b2c0e7ac --- /dev/null +++ b/src/api/client_server/oidc/register.rs @@ -0,0 +1,93 @@ +use oxide_auth::primitives::prelude::Client; +use axum::Json; +use crate::{services, Error, Result}; +use ruma::{ + DeviceId, + api::client::error::ErrorKind as ClientErrorKind, +}; +use reqwest::Url; + +/// The required parameters to register a new client for OAuth2 application. +#[derive(serde::Deserialize, Clone)] +pub 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, + /// Redirect URIs declared by the client. At least one. + redirect_uris: Vec, + /// Must be ["code"]. + response_types: Vec, + /// Must include "authorization_type" and "refresh_token". + grant_types: Vec, + //contacts: Vec, + /// Can be "none". + token_endpoint_auth_method: String, + /// Link to the logo. + logo_uri: Option, + /// Link to the client's policy. + policy_uri: Option, + /// Link to the terms of service. + tos_uri: Option, + /// Defaults to "web" if not present. + application_type: Option, +} + +/// A successful response that the client was registered. +#[derive(serde::Serialize)] +pub struct ClientResponse { + client_id: String, + client_name: String, + client_uri: Url, + logo_uri: Option, + tos_uri: Option, + policy_uri: Option, + redirect_uris: Vec, + token_endpoint_auth_method: String, + response_types: Vec, + grant_types: Vec, + application_type: Option, +} + +/// # `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. +/// +/// [MSC2966]: https://github.com/matrix-org/matrix-spec-proposals/pull/2966 +pub async fn register_client( + Json(client): Json, +) -> Result> { + let Some(redirect_uri) = client.redirect_uris.first().cloned() else { + return Err(Error::BadRequest( + ClientErrorKind::Unknown, + "register request should contain at least a redirect_uri" + )); + }; + let device_id = DeviceId::new(); + let scope = format!( + "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(), + redirect_uri.into(), + scope.parse().expect("device ID should parse in Matrix scope"), + ))?; + + Ok(Json(ClientResponse { + client_id: device_id.to_string(), + client_name: client.client_name.clone(), + client_uri: client.client_uri.clone(), + redirect_uris: client.redirect_uris.clone(), + logo_uri: client.logo_uri.clone(), + policy_uri: client.policy_uri.clone(), + tos_uri: client.tos_uri.clone(), + 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(), + })) +} diff --git a/src/main.rs b/src/main.rs index be0cb7b5..4ee4fcf7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -449,6 +449,9 @@ fn routes(config: &Config) -> Router { .route( "/_matrix/client/unstable/org.matrix.msc2964/token", post(client_server::token) + ) + .route("/_matrix/client/unstable/org.matrix.msc2964/device/register", + post(client_server::register_client) ) .route( "/_matrix/client/r0/rooms/:room_id/initialSync", diff --git a/src/service/oidc/mod.rs b/src/service/oidc/mod.rs index b9a1579c..a6a4ecf6 100644 --- a/src/service/oidc/mod.rs +++ b/src/service/oidc/mod.rs @@ -34,6 +34,16 @@ pub struct Service { } impl Service { + pub fn register_client(&self, client: &Client) -> Result<()> { + self + .registrar + .lock() + .expect("lockable registrar") + .register_client(client.clone()); + + Ok(()) + } + pub fn preconfigured() -> Self { Service { registrar: Mutex::new(