[PR #8686] feat(oauth-provider): add validateRedirectUri option for custom redirect validation #25047

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

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/8686
Author: @lennondotw
Created: 3/19/2026
Status: 🔄 Open

Base: nextHead: feat/custom-redirect-uri-validation


📝 Commits (10+)

  • dd6528f docs(guides): move dynamic base url from concepts to guides and options (#9145)
  • acbd6ef fix: honor forceAllowId UUIDs on postgres adapters (#9068)
  • 7be52d9 docs: add docs email normalization on sentinel (#9188)
  • a0caa1f docs(device-authorization): fix approval page query param (#9197)
  • 9aed910 fix(two-factor): revert enforcement broadening from #9122 (#9205)
  • ba03fb5 chore(deps): bump electron and next devDependencies to patched versions (#9166)
  • 39d6af2 chore(adapters): require patched drizzle-orm and kysely peer versions (#9165)
  • 1b4bec1 feat(oauth-provider): add validateRedirectUri option for custom redirect validation
  • e681d5d chore: add changeset for validateRedirectUri feature
  • efca9fe fix(oauth-provider): make validateRedirectUri extend default validation

📊 Changes

25 files changed (+862 additions, -594 deletions)

View changed files

.changeset/custom-redirect-uri-validation.md (+7 -0)
.changeset/fix-revert-2fa-broadening.md (+9 -0)
.changeset/force-allow-id-uuid.md (+5 -0)
.changeset/tighten-adapter-peerdeps.md (+9 -0)
📝 docs/components/sidebar-content.tsx (+6 -6)
📝 docs/content/blogs/1-5.mdx (+1 -1)
docs/content/docs/concepts/dynamic-base-url.mdx (+0 -261)
docs/content/docs/guides/dynamic-base-url.mdx (+155 -0)
📝 docs/content/docs/infrastructure/plugins/sentinel.mdx (+21 -0)
📝 docs/content/docs/plugins/device-authorization.mdx (+1 -1)
📝 docs/content/docs/plugins/oauth-provider.mdx (+55 -0)
📝 docs/content/docs/reference/options.mdx (+38 -1)
📝 e2e/adapter/test/adapter-factory/adapter-factory.test.ts (+35 -0)
📝 packages/better-auth/package.json (+2 -2)
📝 packages/better-auth/src/db/db.test.ts (+51 -0)
📝 packages/better-auth/src/plugins/two-factor/index.ts (+3 -8)
📝 packages/better-auth/src/plugins/two-factor/two-factor.test.ts (+20 -15)
📝 packages/core/src/db/adapter/get-id-field.ts (+2 -2)
📝 packages/drizzle-adapter/package.json (+1 -1)
📝 packages/electron/package.json (+1 -1)

...and 5 more files

📄 Description

Summary

Add a validateRedirectUri option to the OAuth provider that allows custom redirect URI validation logic. This enables use cases like wildcard pattern matching for preview deployments without requiring database modification or workarounds.

Motivation

Platforms like Vercel, Netlify, and Cloudflare Pages generate unique subdomains for each PR/branch preview deployment (e.g., pr-123.preview.example.com, deploy-preview-456.netlify.app). Currently, Better Auth only supports exact string matching for redirect URIs, which means:

  1. Each preview URL must be registered individually (impractical)
  2. Users resort to workarounds like database hooks to temporarily inject URIs (security concerns)

This PR provides a clean, secure API to implement custom validation logic.

Use Cases

  • Preview deployments: Match *.preview.example.com for CI/CD preview URLs
  • Multi-tenant applications: Match *.tenants.example.com for tenant subdomains
  • Development environments: Match localhost with any port

Changes

packages/oauth-provider/src/types/index.ts

  • Added validateRedirectUri option to OAuthOptions interface
  • Comprehensive JSDoc documentation with use cases and security considerations

packages/oauth-provider/src/authorize.ts

  • Modified redirect URI validation to use custom validator when provided
  • Added try-catch to handle validator exceptions (fail-closed for security)
  • Falls back to exact string matching when no validator is provided (backward compatible)

packages/oauth-provider/src/authorize.test.ts

  • Added test suite for custom validateRedirectUri functionality
  • Tests cover: acceptance of matching URIs, rejection of non-matching URIs, exception handling

docs/content/docs/plugins/oauth-provider.mdx

  • Added "Custom Redirect URI Validation" section
  • Includes code examples and security warnings

API

oauthProvider({
  validateRedirectUri: (redirectUri, registeredUris) => {
    // Return true if valid, false otherwise
    // Example: implement wildcard matching
    return registeredUris.some((pattern) => {
      if (!pattern.includes('*')) return pattern === redirectUri;
      const regex = new RegExp('^' + pattern.replace(/\*/g, '[^.]+') + '$');
      return regex.test(redirectUri);
    });
  },
})

Security Considerations

The implementation follows security best practices:

  1. Fail-closed: If no validator is provided, exact string matching is used
  2. Exception handling: If the custom validator throws, the request is rejected (fail-closed)
  3. No information leakage: Invalid redirect URIs are redirected to an error page, not to the attacker-controlled URI
  4. Documentation warnings: Clear warnings about public suffixes, HTTPS requirements, and overly broad patterns

Test Plan

  • Existing tests pass
  • New tests for custom validator functionality
  • Test for validator exception handling (fail-closed)
  • Lint and format pass
  • Build succeeds

Breaking Changes

None. This is a purely additive change with full backward compatibility.


🔄 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/8686 **Author:** [@lennondotw](https://github.com/lennondotw) **Created:** 3/19/2026 **Status:** 🔄 Open **Base:** `next` ← **Head:** `feat/custom-redirect-uri-validation` --- ### 📝 Commits (10+) - [`dd6528f`](https://github.com/better-auth/better-auth/commit/dd6528f45b5593f77ee3503abd760beb5605e651) docs(guides): move dynamic base url from concepts to guides and options (#9145) - [`acbd6ef`](https://github.com/better-auth/better-auth/commit/acbd6ef69f88ea54174446ac0465a426bad7ca09) fix: honor forceAllowId UUIDs on postgres adapters (#9068) - [`7be52d9`](https://github.com/better-auth/better-auth/commit/7be52d9403407378b9c2434d9f7419b51495b308) docs: add docs email normalization on sentinel (#9188) - [`a0caa1f`](https://github.com/better-auth/better-auth/commit/a0caa1f6f8b2b9028b2917c8515280426ed2dbe4) docs(device-authorization): fix approval page query param (#9197) - [`9aed910`](https://github.com/better-auth/better-auth/commit/9aed910499eb4cbc3dd0c395ff5534893daab7a4) fix(two-factor): revert enforcement broadening from #9122 (#9205) - [`ba03fb5`](https://github.com/better-auth/better-auth/commit/ba03fb59f4d1ce4309ddf5fa315d4b2d82951990) chore(deps): bump electron and next devDependencies to patched versions (#9166) - [`39d6af2`](https://github.com/better-auth/better-auth/commit/39d6af2a392dc41018a036d1d909dc48c09749c9) chore(adapters): require patched drizzle-orm and kysely peer versions (#9165) - [`1b4bec1`](https://github.com/better-auth/better-auth/commit/1b4bec13adac0a710034536e7f73fca1f67a0f89) feat(oauth-provider): add validateRedirectUri option for custom redirect validation - [`e681d5d`](https://github.com/better-auth/better-auth/commit/e681d5d1f385495f615e36fc0e537b6c4002d0af) chore: add changeset for validateRedirectUri feature - [`efca9fe`](https://github.com/better-auth/better-auth/commit/efca9fe18f7802c363ad8abb0b9cafa556795f50) fix(oauth-provider): make validateRedirectUri extend default validation ### 📊 Changes **25 files changed** (+862 additions, -594 deletions) <details> <summary>View changed files</summary> ➕ `.changeset/custom-redirect-uri-validation.md` (+7 -0) ➕ `.changeset/fix-revert-2fa-broadening.md` (+9 -0) ➕ `.changeset/force-allow-id-uuid.md` (+5 -0) ➕ `.changeset/tighten-adapter-peerdeps.md` (+9 -0) 📝 `docs/components/sidebar-content.tsx` (+6 -6) 📝 `docs/content/blogs/1-5.mdx` (+1 -1) ➖ `docs/content/docs/concepts/dynamic-base-url.mdx` (+0 -261) ➕ `docs/content/docs/guides/dynamic-base-url.mdx` (+155 -0) 📝 `docs/content/docs/infrastructure/plugins/sentinel.mdx` (+21 -0) 📝 `docs/content/docs/plugins/device-authorization.mdx` (+1 -1) 📝 `docs/content/docs/plugins/oauth-provider.mdx` (+55 -0) 📝 `docs/content/docs/reference/options.mdx` (+38 -1) 📝 `e2e/adapter/test/adapter-factory/adapter-factory.test.ts` (+35 -0) 📝 `packages/better-auth/package.json` (+2 -2) 📝 `packages/better-auth/src/db/db.test.ts` (+51 -0) 📝 `packages/better-auth/src/plugins/two-factor/index.ts` (+3 -8) 📝 `packages/better-auth/src/plugins/two-factor/two-factor.test.ts` (+20 -15) 📝 `packages/core/src/db/adapter/get-id-field.ts` (+2 -2) 📝 `packages/drizzle-adapter/package.json` (+1 -1) 📝 `packages/electron/package.json` (+1 -1) _...and 5 more files_ </details> ### 📄 Description ## Summary Add a `validateRedirectUri` option to the OAuth provider that allows custom redirect URI validation logic. This enables use cases like wildcard pattern matching for preview deployments without requiring database modification or workarounds. ## Motivation Platforms like **Vercel**, **Netlify**, and **Cloudflare Pages** generate unique subdomains for each PR/branch preview deployment (e.g., `pr-123.preview.example.com`, `deploy-preview-456.netlify.app`). Currently, Better Auth only supports exact string matching for redirect URIs, which means: 1. Each preview URL must be registered individually (impractical) 2. Users resort to workarounds like database hooks to temporarily inject URIs (security concerns) This PR provides a clean, secure API to implement custom validation logic. ## Use Cases - **Preview deployments**: Match `*.preview.example.com` for CI/CD preview URLs - **Multi-tenant applications**: Match `*.tenants.example.com` for tenant subdomains - **Development environments**: Match `localhost` with any port ## Changes ### `packages/oauth-provider/src/types/index.ts` - Added `validateRedirectUri` option to `OAuthOptions` interface - Comprehensive JSDoc documentation with use cases and security considerations ### `packages/oauth-provider/src/authorize.ts` - Modified redirect URI validation to use custom validator when provided - Added try-catch to handle validator exceptions (fail-closed for security) - Falls back to exact string matching when no validator is provided (backward compatible) ### `packages/oauth-provider/src/authorize.test.ts` - Added test suite for custom `validateRedirectUri` functionality - Tests cover: acceptance of matching URIs, rejection of non-matching URIs, exception handling ### `docs/content/docs/plugins/oauth-provider.mdx` - Added "Custom Redirect URI Validation" section - Includes code examples and security warnings ## API ```typescript oauthProvider({ validateRedirectUri: (redirectUri, registeredUris) => { // Return true if valid, false otherwise // Example: implement wildcard matching return registeredUris.some((pattern) => { if (!pattern.includes('*')) return pattern === redirectUri; const regex = new RegExp('^' + pattern.replace(/\*/g, '[^.]+') + '$'); return regex.test(redirectUri); }); }, }) ``` ## Security Considerations The implementation follows security best practices: 1. **Fail-closed**: If no validator is provided, exact string matching is used 2. **Exception handling**: If the custom validator throws, the request is rejected (fail-closed) 3. **No information leakage**: Invalid redirect URIs are redirected to an error page, not to the attacker-controlled URI 4. **Documentation warnings**: Clear warnings about public suffixes, HTTPS requirements, and overly broad patterns ## Test Plan - [x] Existing tests pass - [x] New tests for custom validator functionality - [x] Test for validator exception handling (fail-closed) - [x] Lint and format pass - [x] Build succeeds ## Breaking Changes None. This is a purely additive change with full backward compatibility. --- <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:42:18 -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#25047