fix(auth): normalize Workers preview requests

This commit is contained in:
saltbo
2026-08-02 20:27:31 -04:00
parent 783b6aa8fa
commit 02009f098f
2 changed files with 35 additions and 4 deletions
+21 -1
View File
@@ -11,7 +11,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import { createInviteRepo } from './adapters/repos/invite.js'
import { createSiteInvitationRepo } from './adapters/repos/site-invitations.js'
import { createApp } from './app.js'
import { createAuth } from './auth.js'
import { canonicalAuthRequest, createAuth } from './auth.js'
import * as authSchema from './db/auth-schema.js'
import * as schema from './db/schema.js'
import { inviteCodes, siteInvitations } from './db/schema.js'
@@ -759,6 +759,26 @@ describe('Cloudflare Workers preview auth origins', () => {
const commitOrigin = 'https://99dc50ae-zpan.saltbo.workers.dev'
const branchOrigin = 'https://feat-x402-paid-agent-uploads-zpan.saltbo.workers.dev'
it('normalizes official preview requests to the configured authorization server URL', async () => {
const request = new Request(`${branchOrigin}/api/auth/get-session`, {
headers: { Origin: branchOrigin, 'X-Request-ID': 'preview-request' },
})
const normalized = canonicalAuthRequest(request, configuredOrigin)
expect(normalized.url).toBe(`${configuredOrigin}/api/auth/get-session`)
expect(normalized.headers.get('origin')).toBe(branchOrigin)
expect(normalized.headers.get('x-request-id')).toBe('preview-request')
const canonicalRequest = new Request(`${configuredOrigin}/api/auth/get-session`)
expect(canonicalAuthRequest(canonicalRequest, configuredOrigin)).toBe(canonicalRequest)
const post = canonicalAuthRequest(
new Request(`${commitOrigin}/api/auth/oauth2/token`, { method: 'POST', body: 'grant_type=authorization_code' }),
configuredOrigin,
)
expect(post.method).toBe('POST')
expect(await post.text()).toBe('grant_type=authorization_code')
})
it('accepts official commit and branch aliases on the same cached auth instance', async () => {
const ctx = await createTestApp()
const auth = await createAuth(ctx.platform, 'test-secret', configuredOrigin, [configuredOrigin])
+14 -3
View File
@@ -173,6 +173,16 @@ export function officialWorkersPreviewOrigin(
}
}
export function canonicalAuthRequest(request: Request, baseURL: string | undefined): Request {
const requestUrl = new URL(request.url)
if (!officialWorkersPreviewOrigin(baseURL, requestUrl.origin)) return request
const canonicalUrl = new URL(baseURL!)
requestUrl.protocol = canonicalUrl.protocol
requestUrl.host = canonicalUrl.host
return new Request(requestUrl, request)
}
// One query loads every oauth_provider_* row. Configs are snapshotted at auth
// instance creation: better-auth resolves social providers eagerly during its
// context init, so per-request dynamic loading is not possible anyway. Admin
@@ -951,15 +961,16 @@ export async function createAuth(
const defaultAuth = await createAuthInstance(false)
let verificationAuth: typeof defaultAuth | null = null
const dynamicHandler = async (request: Request): Promise<Response> => {
if (!usesEmailVerificationPolicy(request)) return defaultAuth.handler(request)
const authRequest = canonicalAuthRequest(request, baseURL)
if (!usesEmailVerificationPolicy(authRequest)) return defaultAuth.handler(authRequest)
const required = isEmailVerificationRequired(
await systemOptionsRepo.getValue(EMAIL_VERIFICATION_REQUIRED_OPTION_KEY),
)
if (!required) return defaultAuth.handler(request)
if (!required) return defaultAuth.handler(authRequest)
verificationAuth ??= await createAuthInstance(true)
return verificationAuth.handler(request)
return verificationAuth.handler(authRequest)
}
return new Proxy(defaultAuth, {