Register full redirect URIs and compare them by exact string match, with no wildcards or partial paths. RFC 9700 requires exact matching on the authorization server, with one exception for the port in native app loopback redirects. Loose matching is how authorization codes end up on attacker pages.
Why the redirect URI matters
After the user approves a request, the authorization server sends the browser back with an authorization code or token in the URL. If an attacker can make the server redirect to a URL they control, they receive the code. Everything else in the flow depends on that redirect going only where the client intended.
The rule
RFC 9700 section 2.1 says authorization servers MUST use exact string matching for redirect URIs. The only exception is the port in localhost redirect URIs for native apps. The same document notes that pattern based matching has enabled real attacks.
Registration checklist
- Register every redirect URI in full, including scheme, host, port if any, and path.
- Do not allow wildcards in hosts, such as a star in front of a domain.
- Do not allow a prefix match on paths. A registered
/callbackshould not also accept/callback/../other. - Use https for web redirects. Reserve custom schemes and loopback for native apps.
- Keep separate registrations for staging and production, and remove old ones.
- Review registrations when a domain is retired. A dangling subdomain that points to nothing is something an attacker can claim.
Open redirectors
Even with exact matching, a registered page can bounce users to any address given in a query parameter. RFC 9700 says clients and authorization servers must not expose URLs that redirect the browser to arbitrary URIs taken from a query parameter. Audit your callback and login pages for a next or return_to parameter that is not restricted to your own paths.
The authorization server should also authenticate the user before redirecting, and auto redirect only to trusted URIs.
Native apps and loopback
Desktop and CLI tools often listen on a localhost port chosen at runtime. That is the one place where the spec allows the port to vary. Everything else, including the scheme, host and path, still has to match.
Test it
Send an authorization request with each of these and expect a refusal:
- the registered URI plus an extra path segment
- the registered host with a different subdomain
- the registered URI with a different scheme
- the registered URI with a query string added, unless you registered one
If any of them redirect, your matching is too loose.
Quick check
Does PKCE make redirect URI checks unnecessary?
No. PKCE protects against a stolen code being redeemed. Exact redirect matching keeps the code from being sent to the wrong place at all. Use both.
Can I allow a wildcard for preview environments?
It is risky, because any host matching the pattern becomes a valid destination. Register each preview URL explicitly, or use a dedicated test authorization server.
Sources
Last reviewed: 19 October 2026.