Preventing Percent Encoding with URLSearchParams

Viewed 3009

I am trying to use the Node URL and URLSearchParams APIs to make URL building a bit simpler. I'm using it to build a URL like this:

https://<ye olde oauth host>/oauth/authorize?response_type=code&client_id=<my client id>&redirect_uri=https://localhost:4200&scopes=scope1%20scope2

However, my code below is creating the URL like this:

https://<ye olde oauth host>/oauth/authorize?response_type=code&client_id=<my client id>&redirect_uri=https%3A%2F%2Flocalhost%3A4200&scopes=scope1%20scope2

It's my understanding that the URLSearchParams API will percent-encode strings, but what if I don't want them to be encoded, like URLs? Here is my code:

const loginURL = new URL('https://<ye olde oauth host>');
    url.pathname = 'oauth/authorize';
    url.search = new URLSearchParams({
      response_type: 'code',
      client_id: '<my client id>'
    }).toString();
loginURL.searchParams.append('redirect_uri', redirectURI);
loginURL.searchParams.append('scopes', scopes);

The reason I don't want the redirect_uri to be percent encoded is because the OAuth API on the receiving end doesn't know how to parse it. Is there any way using the URLSearchParams to stop it from encoding?

2 Answers

I've had a similar issue. Some percentage symbols were causing me trouble to send a token (the token couldn't be parsed correctly). At the end I just used decodeURIComponent(url) just before the request was sent.

As the browser spec describes, both URL and URLSearchParams encodes param data when building URLs (using slightly different encoding rules).

The only way I know to get unencoded param values out of URLSearchParams is by looking up a specific param value (using .get, .forEach, or a similar method). For example:

const params = new URLSearchParams({
  redirect_uri: 'https://localhost:4200&scopes=scope1 scope2'
});

params.toString();
// Returns encoded: 'redirect_uri=https%3A%2F%2Flocalhost%3A4200%26scopes%3Dscope1+scope2'

params.get('redirect_uri');
// Returns unencoded: 'https://localhost:4200&scopes=scope1 scope2'

You could use this to construct your own urls manually but that sort of defeats the purpose of these APIs.

Some characters just make the URL invalid and you can't really get around encoding them. Here's some good docs on valid URLs and encodings.

Even if you construct an unencoded URL, when you go to make the request, fetch (and other fetch-like libraries) will often just encode it for you.

On the plus side, many server frameworks expect search params to be encoded, and will decode the params automatically when receiving them. If your OAuth service doesn't do that already then it probably should!

Related