Preflight checklist
Ory Network Project
No response
Describe your problem
Fosite's dynamic port matching for loopback redirect URIs (per RFC 8252 §7.3) works for 127.0.0.1 and [::1] but not localhost. A client that registers http://localhost/callback cannot use http://localhost:54321/callback at authorization time.
The root cause is isLoopbackAddress, which uses net.ParseIP(hostname).IsLoopback(). Since net.ParseIP("localhost") returns nil, localhost is never recognized as loopback for port matching.
Fosite does handle localhost elsewhere: IsLocalhost explicitly checks hn == "localhost" to allow plain HTTP redirects. But that function is only used for transport security checks, not port matching.
RFC 8252 §7.3 strictly specifies IP literals for loopback URIs, and §8.3 says localhost is NOT RECOMMENDED. However, since Fosite already accepts localhost for security purposes, and real-world clients (e.g. MCP/OAuth flows) register http://localhost/callback by convention, extending port matching to cover localhost would make the two code paths consistent.
Describe your ideal solution
Add localhost handling to isLoopbackAddress:
func isLoopbackAddress(hostname string) bool {
if hostname == "localhost" {
return true
}
return net.ParseIP(hostname).IsLoopback()
}
The existing hostname equality check in isMatchingAsLoopback (registered.Hostname() == requested.Hostname()) already prevents cross-matching between localhost and IP literals.
Workarounds or alternatives
- Register IP literals instead of
localhost: Use http://127.0.0.1/callback as the RFC recommends. Requires updating existing clients that use localhost by convention.
- Patch downstream: Override redirect URI matching in each Fosite consumer.
- Register the exact port: Use
http://localhost:54321/callback. Defeats dynamic port allocation.
Version
v0.49.0
Additional Context
Preflight checklist
Ory Network Project
No response
Describe your problem
Fosite's dynamic port matching for loopback redirect URIs (per RFC 8252 §7.3) works for
127.0.0.1and[::1]but notlocalhost. A client that registershttp://localhost/callbackcannot usehttp://localhost:54321/callbackat authorization time.The root cause is
isLoopbackAddress, which usesnet.ParseIP(hostname).IsLoopback(). Sincenet.ParseIP("localhost")returnsnil,localhostis never recognized as loopback for port matching.Fosite does handle
localhostelsewhere:IsLocalhostexplicitly checkshn == "localhost"to allow plain HTTP redirects. But that function is only used for transport security checks, not port matching.localhost?IsLocalhostisLoopbackAddressRFC 8252 §7.3 strictly specifies IP literals for loopback URIs, and §8.3 says
localhostis NOT RECOMMENDED. However, since Fosite already acceptslocalhostfor security purposes, and real-world clients (e.g. MCP/OAuth flows) registerhttp://localhost/callbackby convention, extending port matching to coverlocalhostwould make the two code paths consistent.Describe your ideal solution
Add
localhosthandling toisLoopbackAddress:The existing hostname equality check in
isMatchingAsLoopback(registered.Hostname() == requested.Hostname()) already prevents cross-matching betweenlocalhostand IP literals.Workarounds or alternatives
localhost: Usehttp://127.0.0.1/callbackas the RFC recommends. Requires updating existing clients that uselocalhostby convention.http://localhost:54321/callback. Defeats dynamic port allocation.Version
v0.49.0
Additional Context
localhostNOT RECOMMENDEDisLoopbackAddress: Root causeIsLocalhost: ExistinglocalhosthandlingisMatchingAsLoopback: Port-flexible matching logic