Skip to content

feat: add wildcard CORS origin pattern matching for better-auth compatibility#1152

Merged
hotlong merged 2 commits intomainfrom
claude/add-better-auth-cors-support
Apr 15, 2026
Merged

feat: add wildcard CORS origin pattern matching for better-auth compatibility#1152
hotlong merged 2 commits intomainfrom
claude/add-better-auth-cors-support

Conversation

@Claude
Copy link
Copy Markdown
Contributor

@Claude Claude AI commented Apr 15, 2026

The Hono server plugin's CORS middleware did not support wildcard origin patterns like https://*.objectui.org,https://*.objectstack.ai used by better-auth's trustedOrigins configuration.

Changes

Pattern Matching Implementation

  • Added matchOriginPattern() and createOriginMatcher() functions to support wildcard patterns in CORS origins
  • Supports subdomain wildcards (https://*.objectui.org)
  • Supports port wildcards (http://localhost:*)
  • Supports comma-separated patterns (https://*.objectui.org,https://*.objectstack.ai)

CORS Middleware Enhancement

  • Enhanced CORS initialization to detect wildcard patterns and apply pattern matching
  • Backward compatible with exact origin matching
  • Works with both cors.origins option and CORS_ORIGIN environment variable

Testing & Documentation

  • Added comprehensive unit tests for pattern matching functions
  • Added integration tests for CORS middleware configuration
  • Updated README with CORS configuration examples

Usage

// Wildcard subdomain patterns
new HonoServerPlugin({
  cors: {
    origins: ['https://*.objectui.org', 'https://*.objectstack.ai'],
    credentials: true
  }
})

// Via environment variable
// CORS_ORIGIN="https://*.objectui.org,https://*.objectstack.ai"

Claude AI and others added 2 commits April 15, 2026 10:08
- Add matchOriginPattern() and createOriginMatcher() functions to support wildcard patterns
- Support subdomain wildcards (e.g., https://*.objectui.org)
- Support port wildcards (e.g., http://localhost:*)
- Support comma-separated patterns for better-auth compatibility
- Add comprehensive unit tests for pattern matching
- Update CORS middleware to use pattern matching for wildcard origins
- Maintain backward compatibility with exact origin matching

Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/a7ac1ca8-a54d-49a5-8469-ec453eb41b8f

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectstack-demo Ready Ready Preview, Comment Apr 15, 2026 10:11am
spec Error Error Apr 15, 2026 10:11am

Request Review

Copy link
Copy Markdown

@github-advanced-security github-advanced-security AI left a comment

Choose a reason for hiding this comment

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

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@hotlong hotlong marked this pull request as ready for review April 15, 2026 10:23
Copilot AI review requested due to automatic review settings April 15, 2026 10:23
@hotlong hotlong merged commit 1f4dabd into main Apr 15, 2026
10 of 13 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds wildcard-capable CORS origin matching to @objectstack/plugin-hono-server so configurations like better-auth trustedOrigins (e.g., https://*.objectui.org, http://localhost:*) can be used safely and conveniently.

Changes:

  • Introduces wildcard origin matching helpers and wires them into the Hono CORS middleware setup.
  • Expands the plugin test suite with CORS-related coverage and updates the adapter mock to include rawApp.use.
  • Updates the plugin README with wildcard CORS configuration examples (options + env vars).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
packages/plugins/plugin-hono-server/src/pattern-matcher.test.ts Adds unit tests for wildcard origin matching behavior (currently using local re-implementations).
packages/plugins/plugin-hono-server/src/hono-plugin.ts Adds wildcard matching helpers and applies them when configuring hono/cors origin handling.
packages/plugins/plugin-hono-server/src/hono-plugin.test.ts Updates mocks and adds CORS-related plugin init tests.
packages/plugins/plugin-hono-server/README.md Documents wildcard CORS usage patterns and env var examples.

Comment on lines +14 to +57
function matchOriginPattern(origin: string, pattern: string): boolean {
if (pattern === '*') return true;
if (pattern === origin) return true;

// Convert wildcard pattern to regex
// Escape special regex characters except *
const regexPattern = pattern
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special chars
.replace(/\*/g, '.*'); // Convert * to .*

const regex = new RegExp(`^${regexPattern}$`);
return regex.test(origin);
}

/**
* Create a CORS origin matcher function that supports wildcard patterns.
*
* @param patterns Single pattern, array of patterns, or comma-separated patterns
* @returns Function that returns the origin if it matches, or null/undefined
*/
function createOriginMatcher(
patterns: string | string[]
): (origin: string) => string | undefined | null {
// Normalize to array
let patternList: string[];
if (typeof patterns === 'string') {
// Handle comma-separated patterns
patternList = patterns.includes(',')
? patterns.split(',').map(s => s.trim()).filter(Boolean)
: [patterns];
} else {
patternList = patterns;
}

// Return matcher function
return (requestOrigin: string) => {
for (const pattern of patternList) {
if (matchOriginPattern(requestOrigin, pattern)) {
return requestOrigin;
}
}
return null;
};
}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

This test file re-implements matchOriginPattern/createOriginMatcher locally instead of importing the production implementation, so the tests can pass even if the real CORS matcher in hono-plugin.ts is broken or diverges over time. Extract these helpers into a shared module (e.g., src/pattern-matcher.ts) and import them from both hono-plugin.ts and this test to ensure the tests exercise the shipped behavior.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +4 to +11
* Check if an origin matches a pattern with wildcards.
* Supports patterns like:
* - "https://*.example.com" - matches any subdomain
* - "http://localhost:*" - matches any port
* - "https://*.objectui.org,https://*.objectstack.ai" - comma-separated patterns
*
* @param origin The origin to check (e.g., "https://app.example.com")
* @param pattern The pattern to match against (supports * wildcard)
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

The docstring for matchOriginPattern() says it supports comma-separated patterns, but the function matches a single pattern and does not split on commas (comma handling is done in createOriginMatcher). Please update this comment to avoid misleading future readers about what this function accepts.

Suggested change
* Check if an origin matches a pattern with wildcards.
* Supports patterns like:
* - "https://*.example.com" - matches any subdomain
* - "http://localhost:*" - matches any port
* - "https://*.objectui.org,https://*.objectstack.ai" - comma-separated patterns
*
* @param origin The origin to check (e.g., "https://app.example.com")
* @param pattern The pattern to match against (supports * wildcard)
* Check if an origin matches a single pattern with wildcards.
* Supports patterns like:
* - "https://*.example.com" - matches any subdomain
* - "http://localhost:*" - matches any port
*
* @param origin The origin to check (e.g., "https://app.example.com")
* @param pattern The single pattern to match against (supports * wildcard)

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +123
/**
* Check if an origin matches a pattern with wildcards.
* Supports patterns like:
* - "https://*.example.com" - matches any subdomain
* - "http://localhost:*" - matches any port
* - "https://*.objectui.org,https://*.objectstack.ai" - comma-separated patterns
*
* @param origin The origin to check (e.g., "https://app.example.com")
* @param pattern The pattern to match against (supports * wildcard)
* @returns true if origin matches the pattern
*/
function matchOriginPattern(origin: string, pattern: string): boolean {
if (pattern === '*') return true;
if (pattern === origin) return true;

// Convert wildcard pattern to regex
// Escape special regex characters except *
const regexPattern = pattern
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special chars
.replace(/\*/g, '.*'); // Convert * to .*

const regex = new RegExp(`^${regexPattern}$`);
return regex.test(origin);
}

/**
* Create a CORS origin matcher function that supports wildcard patterns.
*
* @param patterns Single pattern, array of patterns, or comma-separated patterns
* @returns Function that returns the origin if it matches, or null/undefined
*/
function createOriginMatcher(
patterns: string | string[]
): (origin: string) => string | undefined | null {
// Normalize to array
let patternList: string[];
if (typeof patterns === 'string') {
// Handle comma-separated patterns
patternList = patterns.includes(',')
? patterns.split(',').map(s => s.trim()).filter(Boolean)
: [patterns];
} else {
patternList = patterns;
}

// Return matcher function
return (requestOrigin: string) => {
for (const pattern of patternList) {
if (matchOriginPattern(requestOrigin, pattern)) {
return requestOrigin;
}
}
return null;
};
}
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

matchOriginPattern()/createOriginMatcher() are embedded in hono-plugin.ts, duplicating logic and making it hard to reuse or unit test the real implementation (tests currently copy/paste their own version). Move these helpers into a dedicated module and import them here; this reduces drift risk and keeps hono-plugin.ts focused on plugin wiring.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +122
function createOriginMatcher(
patterns: string | string[]
): (origin: string) => string | undefined | null {
// Normalize to array
let patternList: string[];
if (typeof patterns === 'string') {
// Handle comma-separated patterns
patternList = patterns.includes(',')
? patterns.split(',').map(s => s.trim()).filter(Boolean)
: [patterns];
} else {
patternList = patterns;
}

// Return matcher function
return (requestOrigin: string) => {
for (const pattern of patternList) {
if (matchOriginPattern(requestOrigin, pattern)) {
return requestOrigin;
}
}
return null;
};
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

createOriginMatcher() compiles a new RegExp on every request for every pattern (via matchOriginPattern()), which is unnecessary overhead under load. Consider pre-compiling patterns once when building the matcher (e.g., convert patterns to RegExp instances up front) and then only running regex.test() per request.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants