From a2e689c548ac8b4f6504f6fa9321e97ae9f0a0b3 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 6 Apr 2026 13:58:27 -0400 Subject: [PATCH] Use url::form_urlencoded for OAuth form body encoding Replace hand-rolled format! string interpolation with url::form_urlencoded::Serializer in exchange_code and refresh_token. The previous code didn't percent-encode the code, verifier, or refresh_token values, which would break if they contained &, =, or +. --- crates/language_models/Cargo.toml | 1 + .../src/provider/openai_subscribed.rs | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/language_models/Cargo.toml b/crates/language_models/Cargo.toml index 1111862ded0e13646d63ac47ebccb9e5e75bec19..e349cf100456087c494d4e4426998f62c6854a1d 100644 --- a/crates/language_models/Cargo.toml +++ b/crates/language_models/Cargo.toml @@ -65,6 +65,7 @@ tiktoken-rs.workspace = true tokio = { workspace = true, features = ["rt", "rt-multi-thread"] } ui.workspace = true ui_input.workspace = true +url.workspace = true util.workspace = true vercel = { workspace = true, features = ["schemars"] } x_ai = { workspace = true, features = ["schemars"] } diff --git a/crates/language_models/src/provider/openai_subscribed.rs b/crates/language_models/src/provider/openai_subscribed.rs index 4a6d9cc9fef8da34349d56ff1d9bab8d427626cc..c9b363216e77ad4c0643ea78fc15300325958432 100644 --- a/crates/language_models/src/provider/openai_subscribed.rs +++ b/crates/language_models/src/provider/openai_subscribed.rs @@ -19,6 +19,7 @@ use smol::io::{AsyncReadExt as _, AsyncWriteExt as _}; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use ui::{ConfiguredApiCard, prelude::*}; +use url::form_urlencoded; use util::ResultExt as _; use crate::provider::open_ai::{OpenAiResponseEventMapper, into_open_ai_response}; @@ -749,10 +750,13 @@ async fn exchange_code( code: &str, verifier: &str, ) -> Result { - let body = format!( - "grant_type=authorization_code&client_id={CLIENT_ID}&code={code}&redirect_uri={encoded_redirect}&code_verifier={verifier}", - encoded_redirect = percent_encode(REDIRECT_URI), - ); + let body = form_urlencoded::Serializer::new(String::new()) + .append_pair("grant_type", "authorization_code") + .append_pair("client_id", CLIENT_ID) + .append_pair("code", code) + .append_pair("redirect_uri", REDIRECT_URI) + .append_pair("code_verifier", verifier) + .finish(); let request = HttpRequest::builder() .method(Method::POST) @@ -778,8 +782,11 @@ async fn refresh_token( client: &Arc, refresh_token: &str, ) -> Result { - let body = - format!("grant_type=refresh_token&client_id={CLIENT_ID}&refresh_token={refresh_token}"); + let body = form_urlencoded::Serializer::new(String::new()) + .append_pair("grant_type", "refresh_token") + .append_pair("client_id", CLIENT_ID) + .append_pair("refresh_token", refresh_token) + .finish(); let request = HttpRequest::builder() .method(Method::POST)