From 2a7455b5c94d4cb214232c21f24ca954ea8aa045 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Fri, 2 Jan 2026 04:11:37 +0000 Subject: [PATCH] Use form-urlencoded bodies for server-to-server oauth requests. (fixes #249) Signed-off-by: Jason Volk --- src/service/oauth/mod.rs | 45 ++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/service/oauth/mod.rs b/src/service/oauth/mod.rs index 4a5ed471..0313ea4e 100644 --- a/src/service/oauth/mod.rs +++ b/src/service/oauth/mod.rs @@ -5,7 +5,10 @@ pub mod user_info; use std::sync::Arc; use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD as b64encode}; -use reqwest::{Method, header::ACCEPT}; +use reqwest::{ + Method, + header::{ACCEPT, CONTENT_TYPE}, +}; use ruma::UserId; use serde::Serialize; use serde_json::Value as JsonValue; @@ -49,12 +52,15 @@ pub async fn request_userinfo( &self, (provider, session): (&Provider, &Session), ) -> Result { + #[derive(Debug, Serialize)] + struct Query; + let url = provider .userinfo_url .clone() .ok_or_else(|| err!(Config("userinfo_url", "Missing userinfo URL in config")))?; - self.request((Some(provider), Some(session)), Method::GET, url) + self.request((Some(provider), Some(session)), Method::GET, url, Option::::None) .await .and_then(|value| serde_json::from_value(value).map_err(Into::into)) .log_err() @@ -66,6 +72,9 @@ pub async fn request_tokeninfo( &self, (provider, session): (&Provider, &Session), ) -> Result { + #[derive(Debug, Serialize)] + struct Query; + let url = provider .introspection_url .clone() @@ -73,7 +82,7 @@ pub async fn request_tokeninfo( err!(Config("introspection_url", "Missing introspection URL in config")) })?; - self.request((Some(provider), Some(session)), Method::GET, url) + self.request((Some(provider), Some(session)), Method::GET, url, Option::::None) .await .and_then(|value| serde_json::from_value(value).map_err(Into::into)) .log_err() @@ -93,17 +102,12 @@ pub async fn revoke_token(&self, (provider, session): (&Provider, &Session)) -> client_secret: &provider.client_secret, }; - let query = serde_html_form::to_string(&query)?; let url = provider .revocation_url .clone() - .map(|mut url| { - url.set_query(Some(&query)); - url - }) .ok_or_else(|| err!(Config("revocation_url", "Missing revocation URL in config")))?; - self.request((Some(provider), Some(session)), Method::POST, url) + self.request((Some(provider), Some(session)), Method::POST, url, Some(query)) .await .log_err() .map(|_| ()) @@ -135,17 +139,12 @@ pub async fn request_token( redirect_uri: provider.callback_url.as_ref().map(Url::as_str), }; - let query = serde_html_form::to_string(&query)?; let url = provider .token_url .clone() - .map(|mut url| { - url.set_query(Some(&query)); - url - }) .ok_or_else(|| err!(Config("token_url", "Missing token URL in config")))?; - self.request((Some(provider), Some(session)), Method::POST, url) + self.request((Some(provider), Some(session)), Method::POST, url, Some(query)) .await .and_then(|value| serde_json::from_value(value).map_err(Into::into)) .log_err() @@ -160,14 +159,18 @@ pub async fn request_token( name = "request", level = "debug", ret(level = "trace"), - skip(self) + skip(self, body) )] -pub async fn request( +pub async fn request( &self, (provider, session): (Option<&Provider>, Option<&Session>), method: Method, url: Url, -) -> Result { + body: Option, +) -> Result +where + Body: Serialize, +{ let mut request = self .services .client @@ -175,6 +178,12 @@ pub async fn request( .request(method, url) .header(ACCEPT, "application/json"); + if let Some(body) = body.map(serde_html_form::to_string).transpose()? { + request = request + .header(CONTENT_TYPE, "application/x-www-form-urlencoded") + .body(body); + } + if let Some(session) = session { if let Some(access_token) = session.access_token.clone() { request = request.bearer_auth(access_token);