Skip to content

client SDK oauth.applications.register declares redirect_uris REQUIRED where the better-auth body schema declares it optional, so a legal vendor call cannot be expressed #17215

Description

@claude

Filed on the domain:cli execution PM seat's instruction (#6024), overruling the delivering dev's not-filed call. Found while delivering #15447 (PR #17209), out of that card's three-member scope and deliberately not fixed there.

The divergence

ObjectStackClient.oauth.applications.register declares redirect_uris required (packages/client/src/index.ts):

register: async (req: {
  client_name?: string;
  redirect_uris: string[];        // no `?` — required
  token_endpoint_auth_method?: 'none' | 'client_secret_basic' | 'client_secret_post';
  ...
})

The route it posts to, POST /api/v1/auth/oauth2/create-client, is mounted verbatim from @better-auth/oauth-provider@1.7.2, and its body schema declares redirect_uris optional, typed as an array of string. Every one of that schema's 21 members is optional.

So the SDK is narrower than the producer, and a request the route would accept — one that omits redirect_uris entirely — cannot be expressed through this method at all. The caller does not get a wrong answer; they get a call they cannot write.

Driven, not inferred

From the measurement round on #15447 (comment 5559384773). The vendor body schema was not transcribed from the package's .d.ts — it was introspected at runtime: instantiate oauthProvider(), walk endpoints, read options.body. Its row for this member, verbatim from that comment's table:

member type
redirect_uris optional array of string ⚠️ optional here; the SDK declares it required

The same round recorded that the object sets no catchall, so it is zod's default strip, and that the member list has no drift at 1.7.2.

Why this is p3 and not p2, stated so the grade is checkable

This is the same family as #15447 — a published SDK declaration that does not match the route it posts to — but in the opposite direction, and the direction is the whole of the grade:

The safe direction is why it is p3. ⛔ It is not a reason the divergence is not real: the declaration still does not match the producer, which is the defect class this repo files.

Not decided here

  1. Relax to optional — matches the producer exactly, and is the answer if the SDK's job is to express the route.
  2. Keep it required, and say so — defensible on its own terms: a client registered with no redirect URI cannot complete an authorization_code flow, so requiring it is a guard rather than an accident. If this is the intent it should be stated in the method's TSDoc, because today the stricter declaration is indistinguishable from an oversight; a deliberate narrowing that says nothing reads as a bug to the next auditor, which is how this card came to exist.
  3. Whichever wins, the answer is worth checking against the two other doors that register a client: the RFC 7591 DCR endpoint POST /oauth2/register, and the ObjectStack mount POST /api/v1/auth/sys-oauth-application/register, which takes redirect URLs as newline-separated text and splits them itself.

Relaxing a required member to optional is not breaking for callers (every existing call still compiles); tightening later would be.

Siblings

All three are readings of the same driven round, comment 5559384773 on #15447, against @better-auth/oauth-provider@1.7.2.


Generated by Claude Code

Activity

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions