Description
OAuth1 proxy requests can receive an invalid signature when an object query parameter contains a space. The object and pre-serialized forms of the same parameters produce different authentication results.
This was reproduced with an X Ads OAuth1 integration through Nango Cloud. No credentials or account identifiers are included below.
Minimal reproduction
Using @nangohq/node 0.70.5:
await nango.proxy({
method: "GET",
endpoint: "/resource",
providerConfigKey: "oauth1-integration",
connectionId: "connection-id",
params: { q: "ai music" }
});
The object form constructs a query containing q=ai+music. With X Ads this returned HTTP 401 UNAUTHORIZED_ACCESS.
Passing the same value as a pre-serialized query changes the authentication outcome:
await nango.proxy({
method: "GET",
endpoint: "/resource",
providerConfigKey: "oauth1-integration",
connectionId: "connection-id",
params: "q=ai%20music"
});
This request passed OAuth authentication and reached the upstream endpoint handler. For the tested X Ads read endpoint, the response was its endpoint-level HTTP 503 SERVICE_UNAVAILABLE, matching a request signed directly with the same credentials. Values without spaces did not produce the 401.
Suspected cause
Current master still appears affected at commit 2e17e5dca772345a21905817537df01c82fccfa7:
buildProxyURL uses URLSearchParams.set and then URL.toString, which serializes spaces as +.
- The OAuth1 request path passes that URL to
oauth-1.0a.
oauth-1.0a parses the query with decodeURIComponent without converting form-encoded + to a space. It consequently signs the value as a literal plus (%2B). The OAuth provider parses the query value as a space and calculates %20, so the signature bases differ.
Expected behavior
Object query parameters and equivalent pre-serialized query parameters should produce the same valid OAuth1 signature.
Suggested fix and regression coverage
Normalize object query parameters to RFC 3986 encoding before both the outbound request and OAuth1 signing, or ensure + is decoded as a space before building the OAuth1 parameter string.
A regression test with { q: "ai music" } should verify that:
- the outbound query and OAuth1 signature use the same decoded value;
- a literal plus remains distinguishable from a space;
- object and pre-serialized
%20 parameter forms produce equivalent OAuth1 signatures.
Description
OAuth1 proxy requests can receive an invalid signature when an object query parameter contains a space. The object and pre-serialized forms of the same parameters produce different authentication results.
This was reproduced with an X Ads OAuth1 integration through Nango Cloud. No credentials or account identifiers are included below.
Minimal reproduction
Using
@nangohq/node0.70.5:The object form constructs a query containing
q=ai+music. With X Ads this returned HTTP 401UNAUTHORIZED_ACCESS.Passing the same value as a pre-serialized query changes the authentication outcome:
This request passed OAuth authentication and reached the upstream endpoint handler. For the tested X Ads read endpoint, the response was its endpoint-level HTTP 503
SERVICE_UNAVAILABLE, matching a request signed directly with the same credentials. Values without spaces did not produce the 401.Suspected cause
Current
masterstill appears affected at commit2e17e5dca772345a21905817537df01c82fccfa7:buildProxyURLusesURLSearchParams.setand thenURL.toString, which serializes spaces as+.oauth-1.0a.oauth-1.0aparses the query withdecodeURIComponentwithout converting form-encoded+to a space. It consequently signs the value as a literal plus (%2B). The OAuth provider parses the query value as a space and calculates%20, so the signature bases differ.Expected behavior
Object query parameters and equivalent pre-serialized query parameters should produce the same valid OAuth1 signature.
Suggested fix and regression coverage
Normalize object query parameters to RFC 3986 encoding before both the outbound request and OAuth1 signing, or ensure
+is decoded as a space before building the OAuth1 parameter string.A regression test with
{ q: "ai music" }should verify that:%20parameter forms produce equivalent OAuth1 signatures.