Skip to content

client SDK oauth.applications.register declares three request members the better-auth body schema silently strips (metadata, name, scopes) #15447

Description

@os-litant

Found while binding the oauth.* return types in #14312 (PR #15445). Request side, not return side — out of that card's scope, filed rather than fixed in passing.

The defect

ObjectStackClient.oauth.applications.register declares this request type (packages/client/src/index.ts):

register: async (req: {
  client_name?: string;
  name?: string;                            // not in the vendor body schema
  redirect_uris: string[];
  token_endpoint_auth_method?: 'none' | 'client_secret_basic' | 'client_secret_post';
  grant_types?: string[];
  response_types?: string[];
  client_uri?: string;
  logo_uri?: string;
  scope?: string;
  scopes?: string[];                        // not in the vendor body schema
  contacts?: string[];
  tos_uri?: string;
  policy_uri?: string;
  metadata?: Record< string, unknown >;     // not in the vendor body schema
})

The route is POST /api/v1/auth/oauth2/create-client, mounted verbatim from @better-auth/oauth-provider (auth-route-ledger.ts records it source: 'better-auth'). Its body schema is a Zod object with z.core.$strip semantics, and its members are: redirect_uris, scope, client_name, client_uri, logo_uri, contacts, tos_uri, policy_uri, software_id, software_version, software_statement, post_logout_redirect_uris, backchannel_logout_uri, backchannel_logout_session_required, token_endpoint_auth_method, application_type, jwks, jwks_uri, grant_types, response_types, dpop_bound_access_tokens.

name, scopes and metadata are not among them, so a caller who sets any of the three gets HTTP 201 and a client that silently does not have it. $strip drops unknown keys rather than refusing them, so there is no error to notice.

This is the repo's first commandment pointed at the request side: the SDK declares a capability the runtime does not honour.

Measured, not inferred

Driven against a real server (real betterAuth + real oauthProvider over the real createObjectQLAdapterFactory on a real ObjectQL engine and a real better-sqlite3 database, real session cookie, real ObjectStackClient). Registering with metadata: { tenant: 'acme', tier: 7 } returned 201 and a body carrying neither key:

{"client_id":"pWhEhQXRmdLVGVZkgiobMJfxEmsgVCoB","client_secret":"EPsjhqMCoPzoNPiQdMRLDPfcUIWDnFmp","client_secret_expires_at":0,"scope":"openid profile email","user_id":"t3IvR7eGC7IHhZnZUy4ftuLgeesecJ4a","client_id_issued_at":1788534576,"client_name":"Probe App","client_uri":"``https://app.example.com","logo_uri":"https://app.example.com/logo.png","contacts":["ops@example.com"],"tos_uri":"https://app.example.com/tos","policy_uri":"https://app.example.com/privacy","redirect_uris":["https://app.example.com/cb"],"token_endpoint_auth_method":"client_secret_basic","grant_types":["authorization_code","refresh_token"],"response_types":["code"],"application_type":"web","disabled":false,"dpop_bound_access_tokens":false}``

metadata was measured directly. name and scopes rest on the body-schema reading above, not on a drive — worth confirming before acting, since the correct disposition may differ per member (name and scopes look like near-misses for the real client_name and scope, which the same type also declares; metadata has no vendor counterpart at all and the serialiser only ever spreads metadata that some other door stored).

Not decided here

Whether each member is deleted, renamed onto its real counterpart, or routed to a door that can honour it is a contract question, and the near-miss pairs (name / client_name, scopes / scope) mean deleting them changes what existing callers compile against. Left to triage.

Version measured: @better-auth/oauth-provider@1.7.2. Nothing here changes the return-type work in #14312 or its two sibling cards, which are return-side only.

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