Skip to content

Validate clientMetadataUrl at construction time rather than during auth flow (SEP-991/CIMD) #1159

Closed

Description

@pcarleton

Problem

The clientMetadataUrl property in OAuthClientProvider is currently only validated during the auth() flow (specifically in authInternal() at src/client/auth.ts:404-407). This means that an invalid URL-based client ID won't be caught until runtime when authentication is attempted.

The current validation:

if (clientMetadataUrl && !isHttpsUrl(clientMetadataUrl)) {
    throw new InvalidClientMetadataError(
        `clientMetadataUrl must be a valid HTTPS URL with a non-root pathname, got: ${clientMetadataUrl}`
    );
}

Suggested Improvement

Move the URL validation to occur earlier - ideally at construction time when the OAuthClientProvider is created. This provides:

  1. Fail-fast behavior: Developers discover configuration errors immediately rather than at runtime
  2. Better debugging: Errors are caught closer to the source of the problem
  3. Clearer error messages: The stack trace points to where the provider was configured, not deep in the auth flow

Possible Approaches

  1. Add validation in a factory function or builder that creates OAuthClientProvider implementations
  2. Document that implementations should validate in their constructor
  3. Provide a utility validation function that can be called at construction time

Related

  • SEP-991: URL-based Client IDs (CIMD)
  • isHttpsUrl() helper function already exists and can be reused

Activity

  1. added
    enhancementRequest for a new feature that's not currently supported
    ready for workEnough information for someone to start working on
    P3Nice to haves, rare edge cases
    authIssues and PRs related to Authentication / OAuth
    on Dec 16, 2025
  2. added a commit that references this issue on Mar 9, 2026
    32c58be
  3. rechedev9 commented on Mar 9, 2026

    @rechedev9
    Contributor

    I've submitted a fix for this in #1653.

    What changed:

    • Added validateClientMetadataUrl() — a thin wrapper around the existing isHttpsUrl() helper that throws OAuthError(InvalidClientMetadata) for invalid URLs
    • Called at all SDK-controlled entry points: StreamableHTTPClientTransport constructor, SSEClientTransport constructor, auth() function, and withOAuth middleware factory
    • Exported as a public utility so consumers implementing OAuthClientProvider directly can call it in their own constructors
    • Existing validation in authInternal() is preserved as defense-in-depth

    This covers approach (1) from the issue (validation at SDK entry points) and approach (3) (exported utility function). Since OAuthClientProvider is an interface, the SDK can't enforce constructor-time validation on external implementations, but the exported utility makes it trivial.

  4. added a commit that references this issue on Mar 27, 2026
    2abde61
  5. pcarleton commented on Mar 30, 2026

    @pcarleton
    MemberAuthor

    Hi @rechedev9 , thanks for your PR. It addresses (3), but I don't think it works well for (1). I'd want to catch it at OAuth provider construction time, so just the uitility function is sufficient, and calling it in example implementations.

    If you can remove the various checks at all entry points, that PR would work otherwise.

  6. added a commit that references this issue on Mar 30, 2026
    738ef45
  7. rechedev9 commented on Mar 30, 2026

    @rechedev9
    Contributor

    @pcarleton Thanks for the feedback — you're right. I've reworked the PR to match what you described:

    What changed:

    • Kept validateClientMetadataUrl() as an exported utility (approach 3)
    • Added the call in InMemoryOAuthClientProvider's constructor as a usage example (approach 1 done correctly — at provider construction time)
    • Removed all entry-point checks from StreamableHTTPClientTransport, SSEClientTransport, auth(), and withOAuth
    • Preserved the existing defense-in-depth validation in authInternal()

    The PR is now much smaller (+113/−2 across 5 files vs the previous +318/−7 across 9). The branch has been rebased on main so the merge conflicts are resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Nice to haves, rare edge casesauthIssues and PRs related to Authentication / OAuthenhancementRequest for a new feature that's not currently supportedready for workEnough information for someone to start working on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions