diff --git a/src/api/client/oidc/mod.rs b/src/api/client/oidc/mod.rs index 3b9604a9..c7bd52e7 100644 --- a/src/api/client/oidc/mod.rs +++ b/src/api/client/oidc/mod.rs @@ -14,8 +14,10 @@ mod discovery; mod login; mod authorize; mod token; +mod register; 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; diff --git a/src/api/client/oidc/register.rs b/src/api/client/oidc/register.rs new file mode 100644 index 00000000..556778c2 --- /dev/null +++ b/src/api/client/oidc/register.rs @@ -0,0 +1,93 @@ +use oxide_auth::primitives::prelude::Client; +use axum::{ + Json, + extract::State, +}; +use conduwuit::{Result, err}; +use ruma::DeviceId; +use reqwest::Url; + +/// 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, + /// 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(crate) 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(crate) async fn register_client( + State(services): State, + Json(client): Json, +) -> Result> { + let Some(redirect_uri) = client.redirect_uris.first().cloned() else { + return Err(err!(Request(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/api/router.rs b/src/api/router.rs index e60c186f..6741fae8 100644 --- a/src/api/router.rs +++ b/src/api/router.rs @@ -129,6 +129,9 @@ pub fn build(router: Router, server: &Server) -> Router { post(client::oidc_login)) .route("/_matrix/client/unstable/org.matrix.msc2964/token", post(client::token)) + // MSC2966 route. + .route("/_matrix/client/unstable/org.matrix.msc2964/device/register", + post(client::register_client)) .ruma_route(&client::send_message_event_route) .ruma_route(&client::send_state_event_for_key_route) .ruma_route(&client::get_state_events_route) diff --git a/src/service/oidc/mod.rs b/src/service/oidc/mod.rs index 57802b54..ebc2118d 100644 --- a/src/service/oidc/mod.rs +++ b/src/service/oidc/mod.rs @@ -45,6 +45,16 @@ impl crate::Service for 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(