[PR #9088] fix(oauth-provider): sort query params before signing to prevent CDN reordering failures #25330

Open
opened 2026-04-15 22:50:20 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/9088
Author: @lawrence3699
Created: 4/10/2026
Status: 🔄 Open

Base: mainHead: fix/oauth-query-param-order


📝 Commits (1)

  • b3e3d3d fix(oauth-provider): sort query params before signing to prevent CDN reordering failures

📊 Changes

5 files changed (+60 additions, -7 deletions)

View changed files

.changeset/sort-hmac-params.md (+5 -0)
📝 packages/oauth-provider/src/authorize.test.ts (+50 -1)
📝 packages/oauth-provider/src/authorize.ts (+2 -0)
📝 packages/oauth-provider/src/client.ts (+1 -6)
📝 packages/oauth-provider/src/utils/index.ts (+2 -0)

📄 Description

Summary

Fixes #8858

The OAuth provider plugin signs query parameters with HMAC but the signature depends on parameter insertion order (URLSearchParams.toString() preserves order). When a CDN or reverse proxy reorders query params in redirect Location headers — which Vercel's edge layer does — the signature verification fails with invalid_signature and the OAuth flow breaks.

Changes

  • authorize.ts (signParams): sort params before computing signature
  • utils/index.ts (verifyOAuthQueryParams): sort params before verification
  • client.ts (parseSignedQuery): stop truncating at sig — the old loop assumed sig was always last, which breaks when params are reordered or sorted

This follows the same approach used by AWS Signature V4 (canonical query string sorting).

Before / After

Before After
CDN reorders params invalid_signature, OAuth flow fails Verification succeeds — params sorted before HMAC
sig not last in URL parseSignedQuery truncates valid params All params preserved

Test plan

Added two regression tests in authorize.test.ts:

  1. Sign params, reverse their order (simulating CDN), verify still passes
  2. Sign params, tamper with a value, verify correctly rejects

All existing tests pass:

  • authorize.test.ts: 20/20
  • oauth.test.ts: 48/48
  • oauthClient/endpoints.test.ts: 10/10
  • pnpm lint:types: all 39 packages pass

Summary by cubic

Sorts OAuth query params before HMAC signing and verification in @better-auth/oauth-provider to make signatures order-independent when CDNs reorder query strings. Also updates client parsing so sig can appear anywhere.

  • Bug Fixes
    • Sort params in authorize.ts and utils/index.ts before computing/verifying signatures to prevent invalid_signature after CDN reordering.
    • Update client.ts to return the full query string instead of truncating at sig.
    • Add tests for reordered params (passes) and tampered params (rejects).

Written for commit b3e3d3d77c. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/9088 **Author:** [@lawrence3699](https://github.com/lawrence3699) **Created:** 4/10/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/oauth-query-param-order` --- ### 📝 Commits (1) - [`b3e3d3d`](https://github.com/better-auth/better-auth/commit/b3e3d3d77c6a08d6da8ed82fb417bae5159a7531) fix(oauth-provider): sort query params before signing to prevent CDN reordering failures ### 📊 Changes **5 files changed** (+60 additions, -7 deletions) <details> <summary>View changed files</summary> ➕ `.changeset/sort-hmac-params.md` (+5 -0) 📝 `packages/oauth-provider/src/authorize.test.ts` (+50 -1) 📝 `packages/oauth-provider/src/authorize.ts` (+2 -0) 📝 `packages/oauth-provider/src/client.ts` (+1 -6) 📝 `packages/oauth-provider/src/utils/index.ts` (+2 -0) </details> ### 📄 Description ## Summary Fixes #8858 The OAuth provider plugin signs query parameters with HMAC but the signature depends on parameter insertion order (`URLSearchParams.toString()` preserves order). When a CDN or reverse proxy reorders query params in redirect `Location` headers — which Vercel's edge layer does — the signature verification fails with `invalid_signature` and the OAuth flow breaks. ## Changes - **`authorize.ts`** (`signParams`): sort params before computing signature - **`utils/index.ts`** (`verifyOAuthQueryParams`): sort params before verification - **`client.ts`** (`parseSignedQuery`): stop truncating at `sig` — the old loop assumed `sig` was always last, which breaks when params are reordered or sorted This follows the same approach used by AWS Signature V4 (canonical query string sorting). ## Before / After | | Before | After | |---|---|---| | CDN reorders params | `invalid_signature`, OAuth flow fails | Verification succeeds — params sorted before HMAC | | `sig` not last in URL | `parseSignedQuery` truncates valid params | All params preserved | ## Test plan Added two regression tests in `authorize.test.ts`: 1. Sign params, reverse their order (simulating CDN), verify still passes 2. Sign params, tamper with a value, verify correctly rejects All existing tests pass: - `authorize.test.ts`: 20/20 - `oauth.test.ts`: 48/48 - `oauthClient/endpoints.test.ts`: 10/10 - `pnpm lint:types`: all 39 packages pass <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Sorts OAuth query params before HMAC signing and verification in `@better-auth/oauth-provider` to make signatures order-independent when CDNs reorder query strings. Also updates client parsing so `sig` can appear anywhere. - **Bug Fixes** - Sort params in `authorize.ts` and `utils/index.ts` before computing/verifying signatures to prevent `invalid_signature` after CDN reordering. - Update `client.ts` to return the full query string instead of truncating at `sig`. - Add tests for reordered params (passes) and tampered params (rejects). <sup>Written for commit b3e3d3d77c6a08d6da8ed82fb417bae5159a7531. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-15 22:50:20 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#25330