From 91f22ad6b4be50b1ab1a9c9b460d54d2194bcfab Mon Sep 17 00:00:00 2001 From: Ronald Ekambi Date: Tue, 14 Apr 2026 08:08:40 -0400 Subject: [PATCH] feat: support dynamic port matching for localhost redirect URIs Add localhost handling to isLoopbackAddress so that native OAuth 2.0 clients registering http://localhost/callback can use dynamic ports (e.g. http://localhost:54321/callback) per RFC 8252 Section 7.3. Previously, isLoopbackAddress only used net.ParseIP which returns nil for the hostname "localhost", preventing port-flexible loopback matching. The existing hostname equality check in isMatchingAsLoopback prevents cross-matching between localhost and IP literals. Closes #873 Made-with: Cursor --- authorize_helper.go | 5 ++++- authorize_helper_test.go | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/authorize_helper.go b/authorize_helper.go index 51bfab2b4..6f5116723 100644 --- a/authorize_helper.go +++ b/authorize_helper.go @@ -141,8 +141,11 @@ func isMatchingAsLoopback(requested *url.URL, registeredURI string) bool { return false } -// Check if address is either an IPv4 loopback or an IPv6 loopback. +// Check if address is either an IPv4 loopback, an IPv6 loopback, or localhost. func isLoopbackAddress(hostname string) bool { + if hostname == "localhost" { + return true + } return net.ParseIP(hostname).IsLoopback() } diff --git a/authorize_helper_test.go b/authorize_helper_test.go index 00a84276b..0d9618ee4 100644 --- a/authorize_helper_test.go +++ b/authorize_helper_test.go @@ -213,6 +213,28 @@ func TestDoesClientWhiteListRedirect(t *testing.T) { isError: false, expected: "https://google.com/?foo=bar%20foo+baz", }, + { + client: &fosite.DefaultClient{RedirectURIs: []string{"http://localhost/callback"}}, + url: "http://localhost:9999/callback", + expected: "http://localhost:9999/callback", + isError: false, + }, + { + client: &fosite.DefaultClient{RedirectURIs: []string{"http://localhost/callback"}}, + url: "http://localhost/callback", + expected: "http://localhost/callback", + isError: false, + }, + { + client: &fosite.DefaultClient{RedirectURIs: []string{"http://127.0.0.1/callback"}}, + url: "http://localhost:9999/callback", + isError: true, + }, + { + client: &fosite.DefaultClient{RedirectURIs: []string{"http://localhost/callback"}}, + url: "http://127.0.0.1:9999/callback", + isError: true, + }, } { redir, err := fosite.MatchRedirectURIWithClientRedirectURIs(c.url, c.client) assert.Equal(t, c.isError, err != nil, "%d: %+v", k, c)