Skip to content

feat(url): support private-use URI scheme redirect URIs (RFC 8252)#12

Closed
Meldiron wants to merge 2 commits into
mainfrom
fix-url-validator-raycast
Closed

feat(url): support private-use URI scheme redirect URIs (RFC 8252)#12
Meldiron wants to merge 2 commits into
mainfrom
fix-url-validator-raycast

Conversation

@Meldiron

Copy link
Copy Markdown
Contributor

What

Adds an opt-in allowPrivateUseSchemes flag to the URL validator so it accepts authority-less private-use URI scheme redirect URIs per RFC 8252 §7.1 (reverse-DNS form), e.g.:

com.raycast-x:/oauth
com.example.app:/oauth2redirect/example-provider

Why

Appwrite Cloud implements OAuth 2.0 Dynamic Client Registration (RFC 7591). Native/desktop clients (e.g. Raycast) register redirect URIs using private-use URI schemes. These are valid URIs (scheme:/path, no authority), but PHP's FILTER_VALIDATE_URL requires an authority component and rejects them, blocking legitimate client registration.

How

  • New trailing constructor flag: public function __construct(..., protected bool $allowPrivateUseSchemes = false). Kept last so existing positional call sites are unaffected.
  • When allowPrivateUseSchemes is on, a value is accepted if it passes the existing filter_var validation or it is a private-use URI scheme redirect URI:
    1. Scheme matches the RFC 3986 grammar (validated explicitly with ctype_*, since parse_url does not enforce it — e.g. it would accept 1com.raycast).
    2. Scheme is a reverse-DNS / private-use scheme (contains a dot), which also excludes standard dotless schemes like http/https.
    3. The authority-less remainder (scheme:/path or scheme:path, plus optional query/fragment) is validated by reusing filter_var with a placeholder authority. The scheme://… authority form is left to the existing logic.
  • allowedSchemes, allowEmpty, and allowFragments continue to apply uniformly, including to private-use URIs.

Backward compatibility

Default behavior is unchanged (allowPrivateUseSchemes defaults to false). All pre-existing tests pass; isValid('http:/example.com') and isValid('example.com') remain false.

Tests

Added testAllowPrivateUseSchemes and testAllowPrivateUseSchemesWithConstraints. Full suite: 135 tests, 878 assertions, green.

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown

Thanks for contributing! This repository is a read-only mirror; development for this library happens in packages/validators in the utopia-php monorepo. Please open this pull request there instead.

@github-actions github-actions Bot closed this Jul 13, 2026
@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds an opt-in allowPrivateUseSchemes flag to the URL validator so it accepts authority-less private-use URI scheme redirect URIs (e.g. com.example.app:/oauth2redirect/…) per RFC 8252 §7.1, enabling native OAuth clients to register such redirect URIs without breaking any existing call sites.

  • Scheme validation uses ctype_* checks against RFC 3986 grammar and requires a dot to distinguish reverse-DNS schemes from dotless standard ones; the authority form (scheme://…) is explicitly redirected back to the existing filter_var path.
  • The authority-less remainder is validated by prepending a placeholder https://localhost authority before calling filter_var, correctly reusing PHP's built-in validation without a regex from scratch.
  • All pre-existing constraints (allowedSchemes, allowFragments, allowEmpty) continue to be enforced on the private-use path through the same downstream checks in isValid.

Confidence Score: 4/5

Safe to merge; the new flag is opt-in and all existing behavior is preserved.

The implementation is correct and well-tested. The only gap is that getDescription() does not mention private-use scheme support when the flag is enabled, which can produce misleading validation error messages for callers.

src/Validator/URL.php — specifically getDescription(), which does not reflect the new allowPrivateUseSchemes flag.

Important Files Changed

Filename Overview
src/Validator/URL.php Adds allowPrivateUseSchemes flag with scheme validation, authority-less remainder validation via placeholder authority, and guards for the //-authority form. Logic is correct; getDescription() doesn't surface the new flag.
tests/Validator/URLTest.php Two new test methods cover the happy path, backward-compatibility, and constraint composition (no-fragments, allowEmpty, allowedSchemes); edge cases like digit-leading schemes and missing dots are exercised.

Comments Outside Diff (1)

  1. src/Validator/URL.php, line 23-39 (link)

    P2 getDescription() silent on private-use scheme support

    When allowPrivateUseSchemes is true, the description returned to callers still reads "Value must be a valid URL" (or the scheme-scoped variant), with no indication that authority-less private-use scheme URIs are accepted. If validation fails for an unrelated reason, clients receive a misleading error message that doesn't hint at what forms are actually allowed.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/Validator/URL.php
    Line: 23-39
    
    Comment:
    **`getDescription()` silent on private-use scheme support**
    
    When `allowPrivateUseSchemes` is `true`, the description returned to callers still reads "Value must be a valid URL" (or the scheme-scoped variant), with no indication that authority-less private-use scheme URIs are accepted. If validation fails for an unrelated reason, clients receive a misleading error message that doesn't hint at what forms are actually allowed.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Codex

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/Validator/URL.php:23-39
**`getDescription()` silent on private-use scheme support**

When `allowPrivateUseSchemes` is `true`, the description returned to callers still reads "Value must be a valid URL" (or the scheme-scoped variant), with no indication that authority-less private-use scheme URIs are accepted. If validation fails for an unrelated reason, clients receive a misleading error message that doesn't hint at what forms are actually allowed.

Reviews (1): Last reviewed commit: "Remove regex" | Re-trigger Greptile

@Meldiron

Copy link
Copy Markdown
Contributor Author

utopia-php/monorepo#60

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.

1 participant