Skip to content

Make loopback redirect port variance opt-in for public clients#58

Merged
ChiragAgg5k merged 2 commits into
mainfrom
feat/auth-loopback-opt-in
Jul 9, 2026
Merged

Make loopback redirect port variance opt-in for public clients#58
ChiragAgg5k merged 2 commits into
mainfrom
feat/auth-loopback-opt-in

Conversation

@ChiragAgg5k

@ChiragAgg5k ChiragAgg5k commented Jul 9, 2026

Copy link
Copy Markdown
Member

What

RedirectUris::matches() now takes an allowLoopback flag (default false). Exact matching always applies — including for loopback URIs — but the RFC 8252 §7.3 any-port loopback fallback only unlocks when the caller opts in.

Why

RFC 8252 scopes the loopback port carve-out to native apps, which are public clients (§8.4) — they bind an ephemeral port per run and cannot register it ahead of time. A confidential client is a server that can hold a secret: it has a stable, routable redirect endpoint and no legitimate need for ephemeral-port loopback redirects, so extending the looseness to it just widens the redirect-target surface. Other implementations gate the same way (e.g. Ory Hydra applies port-insensitive loopback matching only to token_endpoint_auth_method: none clients).

Callers pass their public-client discriminator, e.g.:

$uris->matches($redirectUri, $app->getAttribute('type') === 'public');

Behavior change

The default flips from loose to strict for the loopback port variance introduced in 0.9.0 — releasing as 0.9.1 since 0.9.0 has no merged consumers of the loose behavior. (The Appwrite Cloud adoption PR is unmerged and will pass the flag.)

Testing

  • All fallback cases (positives and strictness negatives) now opt in explicitly
  • New default-strict cases: port variance rejected without opt-in for all three loopback hosts, portless-vs-ported, host-case and path-normalization variants, and an exact loopback match that passes regardless of the flag
  • bin/monorepo test auth — 200 tests, 447 assertions, green; bin/monorepo check auth clean

RFC 8252 scopes the any-port loopback carve-out to native apps, which
are public clients (Section 8.4). Confidential clients have a stable
redirect endpoint and no legitimate need for ephemeral-port loopback
redirects, so RedirectUris::matches() now takes an allowLoopback flag
that is off by default: exact matching always applies, and the port
variance only unlocks when the caller opts in for a public client.
@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes the RFC 8252 §7.3 loopback port-variance opt-in via a new $allowLoopback parameter on RedirectUris::matches() (default false), so confidential clients remain on strict exact matching while public/native clients can enable the ephemeral-port carve-out explicitly.

  • RedirectUris::matches() gains a bool $allowLoopback = false parameter; the loopback port-ignorant path is now gated behind it, leaving exact matching as the universal default.
  • Tests add six new "without opt-in" cases covering all three loopback hosts, port/portless combos, host-case, and path normalization, plus an exact loopback control that passes regardless of the flag; all pre-existing loopback cases now explicitly pass allowLoopback: true.
  • PhraseTest widens its regex to accept hyphens in phrase words (e.g. "Quick-witted"), unrelated to the main change.

Confidence Score: 5/5

Safe to merge — the change narrows the default matching surface and introduces no new code paths that could permit unintended redirects.

The implementation is a clean, minimal guard added before the loopback branch; exact matching is unaffected, and the new flag is default-off so the overall redirect-target surface shrinks rather than expands. The test suite covers all three loopback hosts, both port combinations, case-normalization, path-normalization, and scheme/component strictness with both flag values.

No files require special attention.

Important Files Changed

Filename Overview
packages/auth/src/Auth/OAuth2/RedirectUris.php Adds optional $allowLoopback parameter (default false) to matches(); short-circuits before the port-variance path when the flag is off — logic is correct and well-placed
packages/auth/tests/Auth/OAuth2/RedirectUrisTest.php Extends testMatches to accept $allowLoopback; adds six new default-strict cases and an exact-loopback control; all existing loopback cases now explicitly opt in — coverage is thorough
packages/auth/docs/oauth2.md Updates the matching description and code example to reflect the new opt-in flag; the fourth example comment "host must match" is slightly ambiguous but technically correct
packages/auth/tests/Auth/Proofs/PhraseTest.php Adds - to the phrase regex to accommodate hyphenated words (e.g. "Quick-witted") — straightforward test correction unrelated to the main change

Reviews (2): Last reviewed commit: "(test): allow hyphenated adjectives in p..." | Re-trigger Greptile

Phrase's adjective list includes 'Quick-witted' and 'Fun-loving', so
testGenerate failed whenever the random draw picked one of them.
@ChiragAgg5k ChiragAgg5k merged commit eb1a2ec into main Jul 9, 2026
6 checks passed
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.

2 participants