Skip to content

fix(authorize): build redirect URIs with the WHATWG URL API instead of url.parse() #513 - #515

Open
guruchill wants to merge 1 commit into
node-oauth:masterfrom
guruchill:fix-dep0169-whatwg-url
Open

guruchill wants to merge 1 commit into
node-oauth:masterfrom
guruchill:fix-dep0169-whatwg-url

Conversation

@guruchill

Copy link
Copy Markdown

Summary

Removes the two remaining url.parse() calls, so the authorization endpoint no longer emits Node's runtime deprecation DEP0169. As the maintainer asked in #513, this is option 2, the non-breaking one: the redirect URI is parsed with the WHATWG URL API and returned in the same shape url.parse() gave, and url.format() still serializes it.

  • lib/utils/url-util.js adds parseRedirectUri(redirectUri, parseQueryString). It parses with new URL() and returns the same fields as url.parse(): protocol, slashes, auth, host, port, hostname, hash, search, query, pathname, path, href. With parseQueryString it returns a null-prototype query object, as url.parse(value, true) does.
  • CodeResponseType#buildRedirectUri and AuthorizeHandler#buildErrorRedirectUri call it in place of url.parse(). updateResponse() is unchanged.

Compatibility. I compared the two through the exact steps the library takes (the query / search changes in buildRedirectUri, buildErrorRedirectUri and updateResponse, then url.format()). For both the success and the error redirect, the serialized redirect was identical for these URIs:

  • http(s), with and without a path, port or query;
  • loopback hosts, including IPv6 [::1];
  • userinfo and percent-encoded path and query;
  • an empty query (…/cb?);
  • uppercase hosts;
  • private-use schemes from RFC 8252 (com.example.app:/oauth2redirect, com.example.app://callback).

The one behaviour change: a redirect URI that isn't an absolute URI now throws InvalidArgumentError (Invalid parameter: \redirectUri`), where url.parse()` accepted it. RFC 6749 §3.1.2 requires the redirection endpoint URI to be absolute.

Linked issue(s)

Closes #513. Thanks to @DmitryMarkov for the analysis there, including the two options.

Involved parts of the project

  • The authorization endpoint (AuthorizeHandler), success and error redirects. Authorization Code Grant.
  • CodeResponseType#buildRedirectUri.
  • New internal helper lib/utils/url-util.js, which isn't exported, so index.d.ts is unchanged.
  • docs/api regenerated with npm run docs:api: the updated @throws line, and the new util page.

Added tests?

Yes.

  • test/unit/utils/url-util_test.js covers:
    • the parsed fields and the null-prototype query;
    • the empty-query and IPv6 cases, and RFC 8252 schemes;
    • url.format() output for the URIs above;
    • invalid URIs, and malformed percent-encoding in userinfo.
  • The integration tests check that neither redirect path calls url.parse() (a Sinon spy, through handle() for both a granted and a denied request), and that buildRedirectUri and buildErrorRedirectUri throw on a non-absolute URI.
  • The existing updateResponse() test now builds its input with the helper rather than url.parse().

npm test gives 479 passing (464 before), and the whole run no longer prints DEP0169. npm run lint and npm run format are clean. On npm run test:coverage, url-util.js is at 100% for statements, branches, functions and lines, and the suite overall is at 99.44% lines.

OAuth2 standard

  • RFC 6749 §3.1.2 (absolute redirection URI) and §4.1.2 / §4.1.2.1 (code and error redirects). The parameters added to the redirect are unchanged.
  • RFC 8252 §7.1 (private-use URI schemes) still works.

Reproduction

On Node 24 or later, call OAuth2Server#authorize with a valid response_type=code request. Before this change the first call writes DEP0169 to stderr; after it, nothing is written. npm test shows the same.

🤖 Generated with Claude Code

https://claude.ai/code/session_017dGfgEVLYhZcaAAT8TgTZN

…f url.parse() node-oauth#513

CodeResponseType#buildRedirectUri and AuthorizeHandler#buildErrorRedirectUri
called Node's legacy url.parse(), which Node deprecates at runtime (DEP0169),
so the first authorization request in every process wrote a deprecation
warning to stderr.

Both now use parseRedirectUri() (lib/utils/url-util.js). It parses with
`new URL()` and returns the same fields url.parse() did (protocol, slashes,
auth, host, port, hostname, hash, search, query, pathname, path, href), with
a null-prototype `query` object when asked to parse the query. The documented
return shape is unchanged and url.format() still serializes it, as option 2
in node-oauth#513 proposes, so custom response types and subclasses keep working.

One difference: a redirect URI that isn't an absolute URI (RFC 6749 §3.1.2
requires one) now throws InvalidArgumentError
("Invalid parameter: `redirectUri`"), where url.parse() accepted it.

Tests cover the parsed fields, url.format() output for http(s), loopback,
IPv6, userinfo, percent-encoding and private-use schemes (RFC 8252), an empty
query, invalid URIs, and that neither redirect path calls url.parse(). The
full suite now runs without DEP0169.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dGfgEVLYhZcaAAT8TgTZN

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Authorization endpoint emits DEP0169 url.parse() deprecation warning

1 participant