Skip to content

fix(oauth): normalize authorize resource querystring to string[]#161

Merged
EsTharian merged 1 commit intomainfrom
fix/authorize-resource-querystring-transform
Apr 24, 2026
Merged

fix(oauth): normalize authorize resource querystring to string[]#161
EsTharian merged 1 commit intomainfrom
fix/authorize-resource-querystring-transform

Conversation

@EsTharian
Copy link
Copy Markdown
Member

Summary

End-to-end bug caught after deploying #160: every browser-driven PKCE flow with a single resource query param got invalid_target: requested resource is outside the authorization-code binding at /oauth/token time. Production traffic showed dozens of failed exchanges despite clients correctly sending ?resource=https://mcp.example.com.

Root cause: fastify-type-provider-zod@6.1.0 does not apply Zod .transform() outputs to request.query on GET routes the way it does for POST bodies. resourceParamSchema transforms string → string[], so the schema test (which hands it an array) passed, but real browser traffic (single value → raw string) landed in the handler as a bare string and was dropped when written to the jsonb NOT NULL DEFAULT '[]'::jsonb column.

Token-side binding check then rejected the exchange because authCode.resource was [] while body.resource (POST body transform does work) carried the requested URI.

Fix

Tiny normalizeResource(v: unknown): string[] helper at the top of authorize.ts, called on query.resource before persistence. Schema-level validation (URI shape, no fragment, length caps) still runs — only the array coercion is duplicated.

Test plan

  • New test: query.resource: 'bare-string' (not an array) → code row persisted with resource: ['bare-string']
  • Existing test (array input) still passes — 172/172 auth-server tests
  • Live Claude Code flow: fresh OAuth round-trip produces a token with aud=<resource URI> and memory-mcp accepts it

🤖 Generated with Claude Code

fastify-type-provider-zod@6.1.0 does not apply Zod `.transform()`
outputs to `request.query` on GET routes the way it does for POST
bodies. A single `?resource=X` arrived as the bare string `'X'`, but
the authorize handler passed it straight to
`authorizationCodes.create({ resource: ... })` where the `jsonb`
column expects an array. In practice that meant the auth code stored
`resource: []` (the `'' ?? []` fallback path was never taken — but
the NOT-NULL DEFAULT `'[]'::jsonb` fired and the transform-produced
array was dropped by the ORM somewhere before insert).

End effect: every browser-driven PKCE flow with a single `resource`
param got back a token request the AS then rejected with
`invalid_target: requested resource is outside the authorization-code
binding`, because the token body carried the resource (POST-body
transforms work) but the code row did not.

Fix: introduce a `normalizeResource(v: unknown): string[]` helper at
the top of `authorize.ts` and call it on `query.resource` before
persisting. Schema-level Zod validation still runs (URI shape, no
fragment, length caps) — only the array coercion is duplicated here.

Test: adds an authorize test with `query.resource: 'string-value'`
(not an array) simulating the raw-string arrival, asserting the code
row is created with `resource: ['string-value']`.

172/172 auth-server tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 24, 2026

View your CI Pipeline Execution ↗ for commit b8a73c9

Command Status Duration Result
nx affected -t lint test build ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-24 19:20:48 UTC

@EsTharian EsTharian merged commit 601f96a into main Apr 24, 2026
3 checks passed
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces manual normalization for the resource query parameter in the OAuth authorize route. Due to limitations in fastify-type-provider-zod@6.1.0 where Zod transforms are not applied to GET query parameters, a single resource value could arrive as a string instead of an array. The changes include a new normalizeResource helper function and a corresponding test case to ensure single-value resources are correctly coerced into an array. I have no feedback to provide.

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