fix(auth): use 127.0.0.1 for OAuth loopback redirect instead of localhost#10796
fix(auth): use 127.0.0.1 for OAuth loopback redirect instead of localhost#10796ATKasem wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request addresses an issue where firebase login hangs on dual-stack hosts by changing the OAuth loopback redirect from localhost to 127.0.0.1 and restricting the local server to listen on 127.0.0.1. Review feedback warns that these changes could break other authentication flows, such as GitHub OAuth, which may strictly require localhost or fail on dual-stack hosts when the server does not bind to the IPv6 loopback interface (::1). It is recommended to revert the server binding restriction to maintain compatibility while keeping the IP literal in the redirect URL.
| export function getCallbackUrl(port?: number): string { | ||
| if (typeof port === "undefined") { | ||
| return "urn:ietf:wg:oauth:2.0:oob"; | ||
| } | ||
| return `http://localhost:${port}`; | ||
| // Use the IP literal rather than "localhost" for the OAuth loopback redirect. | ||
| // Google's OAuth 2.0 loopback flow requires an IP literal, and on dual-stack | ||
| // hosts "localhost" can resolve to ::1 while the listener holds the port on | ||
| // 127.0.0.1 (or vice versa), which leaves `firebase login` hanging. | ||
| return `http://127.0.0.1:${port}`; | ||
| } |
There was a problem hiding this comment.
Changing getCallbackUrl to always return 127.0.0.1 might break the GitHub OAuth flow. GitHub's OAuth App configuration is typically configured with a strict redirect URI (often http://localhost). If the registered redirect URI in the GitHub App is http://localhost, sending 127.0.0.1 will result in a redirect_uri_mismatch error.\n\nConsider separating the callback URL generation for Google and GitHub, or ensuring that the GitHub OAuth App configuration is updated to support 127.0.0.1 before rolling this out.
| }); | ||
|
|
||
| server.listen(port, () => { | ||
| server.listen(port, "127.0.0.1", () => { |
There was a problem hiding this comment.
Restricting the local server to listen only on 127.0.0.1 can break authentication flows that still rely on localhost (such as GitHub login, if its OAuth App configuration is strictly registered with http://localhost and cannot easily be changed to 127.0.0.1).\n\nOn dual-stack hosts, the browser may resolve localhost to ::1 (IPv6 loopback) and attempt to connect to [::1]:port. Since the server is strictly listening on 127.0.0.1, this connection will be refused, breaking the GitHub login flow.\n\nBy reverting to server.listen(port) (which binds to all interfaces, including both 127.0.0.1 and ::1), we ensure maximum compatibility for both IPv4 and IPv6 loopback connections, while still resolving the Google OAuth dual-stack issue because the Google redirect URI itself uses the 127.0.0.1 IP literal directly.
| server.listen(port, "127.0.0.1", () => { | |
| server.listen(port, () => { |
943a5ca to
5bd52b6
Compare
…alhost
`firebase login` builds the Google OAuth loopback redirect as
`http://localhost:${port}` and starts the callback server with
`server.listen(port)` (bound to all interfaces). On dual-stack hosts the
browser can resolve `localhost` to ::1 while the listener holds the port on
127.0.0.1 (or a locked-down hosts file remaps `localhost`), so the redirect
never reaches the CLI and login hangs on 'Waiting for authentication...'.
Use the 127.0.0.1 IP literal for both the redirect URI and the listener in the
Google flow, as expected by Google's OAuth 2.0 loopback flow. The GitHub flow
keeps `localhost`, since GitHub validates the redirect_uri host against its
registered OAuth callback URL.
Fixes firebase#10750
5bd52b6 to
e7f9376
Compare
Keep Google redirect on 127.0.0.1 and GitHub on localhost, but listen without a host so dual-stack localhost/::1 clients can still reach the callback (Gemini review on firebase#10796).
|
Addressed the Gemini review feedback:
|
Description
firebase loginbuilds the Google OAuth loopback redirect ashttp://localhost:${port}(getCallbackUrlinsrc/auth.ts).On dual-stack hosts, the browser can resolve
localhostto::1while the OAuth redirect never reaches the CLI, and login hangs onWaiting for authentication....This uses the
127.0.0.1IP literal for the Google redirect URI so it matches Google's OAuth 2.0 loopback requirement (IP literal, notlocalhost).The GitHub login flow stays on
localhost, since GitHub validatesredirect_uriagainst its registered OAuth callback URL.The local callback server still binds with
server.listen(port)(all interfaces), so both127.0.0.1(Google) andlocalhost/::1(GitHub) can reach it on dual-stack hosts.Fixes #10750.
Scenarios Tested
getCallbackUrl(port, "127.0.0.1")→http://127.0.0.1:${port}, default host stayslocalhost, no port yields the OOB URN.npm run mocha -- src/auth.spec.tspasses.Sample Commands
firebase login