Skip to content

sys_oauth_resource.identifier declares an uncited maxLength: 1024, and now over-declares relative to every column that references it #12313

Description

@os-warren

Filed unassigned by the #11701 dev while discharging that card's narrowing condition. Not fixed there: narrowing this column is a contract change on a different published object and would move it off #12198's hash shadow, so it needs its own ruling.

The observation

sys_oauth_resource.identifier (packages/platform-objects/src/identity/sys-oauth-resource.object.ts) declares maxLength: 1024. Two things are true of that number:

  1. It cites no producer. Every bound driver-sql: the platform-objects schema does not sync onto MySQL — unbounded string fields become TEXT, which MySQL refuses to index #11374 declared carries a [#11374] comment naming where it came from (sys_session.token → "better-auth 1.7.1's own MySQL schema … varchar(255)"; sys_oauth_consent.client_id → "a referencing column takes the referenced column's bound"; sys_account.issuer → "transitively from sys_sso_provider.issuer"). This one has no comment at all. git log -S puts it in 4109153 (fix(plugin-auth,platform-objects): close @better-auth/oauth-provider 1.7 schema drift — restore platform SSO #3080), the @better-auth/oauth-provider 1.7 schema-drift close — a parity fix that added the object wholesale, not a sourced-bound pass. It reads as generous slack chosen for "a URI", never derived.

  2. Measured, the producing contract cannot fill it. better-auth 1.7.1 is the sole writer (managedBy: 'better-auth', protection.lock: 'full'). Its own MySQL migration generator (better-auth/dist/db/get-migration.mjs, getType) emits this column as varchar(255) (the field.unique branch — oauthResource.identifier is { type: "string", required: true, unique: true }), and the referring oauthClientResource.resourceId as varchar(36) (the field.references branch). On an upstream MySQL deployment a resource identifier longer than 255 characters cannot be registered at all.

Why it matters now rather than before

#11701 narrowed sys_oauth_client_resource.resource_id from 1024 to 768 so its declared non-unique index could exist on MySQL at all. That leaves the pair asymmetric in the declaration, not just in practice:

column declared physical (MySQL, measured 8.0.46)
sys_oauth_resource.identifier (referent) 1024 text + UNIQUE on a varbinary(32) hash shadow (#12198)
sys_oauth_client_resource.resource_id (referrer) 768 varchar(768), index with SUB_PART = NULL

So on PostgreSQL or SQLite an operator can register a resource whose identifier is 900 characters — the referent's declared contract admits it — and then no client can ever be granted that resource, because the referring column refuses it. Nothing upstream can produce such a value, which is exactly why #11701's narrowing was ruled safe; but the two declarations still disagree about what a legitimate resource identifier is, and only one of them is sourced.

Dispositions worth weighing (not ruled)

  • A — narrow identifier to 768 to match its referrer. Cheapest; keeps the pair consistent; but ⚠️ it also takes sys_oauth_resource off fix(driver-sql): carry an over-long UNIQUE index on a hash-shadow column (MySQL utf8mb4) #12198's hash shadow (768 × 4 = 3072 bytes is exactly the last direct width), changing physical schema on an object that just landed on that route, and reducing the population that route demonstrates.
  • B — narrow both to a sourced 255, matching what upstream can actually store. Most honest against the producer; largest declared-domain reduction, and it rejects values in (255, 768] that the current referrer would accept.
  • C — leave 1024 and document why, i.e. accept the asymmetry and add the citation this column is missing. Cheapest by far, and defensible if the band is agreed unreachable — but it keeps a number nothing derives.

No recommendation carried here — #11701's ruling deliberately scoped itself to the referring column, and picking among these is a fresh decision.

Evidence trail

  • Upstream field declarations: @better-auth/oauth-provider@1.7.1/dist/authorize-*.mjs (resourceId.references = { model: "oauthResource", field: "identifier" }) and dist/oauth-*.d.mts.
  • Upstream width emission: better-auth@1.7.1/dist/db/get-migration.mjs, getType's mysql string branch.
  • Physical readings above are from information_schema.COLUMNS / .STATISTICS on live MySQL 8.0.46 (utf8mb4/InnoDB, STRICT_TRANS_TABLES), read as a separate query rather than from emitted DDL.

Related to #11701 (which narrowed the referrer) and #12198 (which put the referent on a hash shadow).

Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions