Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/void/src/router/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ export function compileRouteTable(

if (auth) {
lines.push(
`const __voidAuthOptions = { dialect: ${JSON.stringify(auth.dialect)}, providers: ${JSON.stringify(auth.providers)}, userConfig: ${auth.authConfigPath ? '__voidAuthConfig' : 'undefined'} };`,
`const __voidAuthOptions = { dialect: ${JSON.stringify(auth.dialect)}, providers: ${JSON.stringify(auth.providers)}, userConfig: ${auth.authConfigPath ? '__voidAuthConfig' : 'undefined'}, dev: ${JSON.stringify(options.dev === true)} };`,
);
if (webSockets.length > 0) {
lines.push(
Expand Down
12 changes: 4 additions & 8 deletions packages/void/src/runtime/better-auth-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type VoidAuthRuntimeOptions = {
dialect: VoidAuthDialect;
providers: Array<VoidAuthProvider> | null;
userConfig?: VoidAuthConfig;
dev?: boolean;
};

export type VoidAuthEnv = Record<string, unknown> & {
Expand Down Expand Up @@ -86,11 +87,7 @@ export type VoidAuthRuntimeApi = {
): Promise<void>;
};

function isLocalHost(hostname: string): boolean {
return hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local');
}

function resolveSecret(request: Request, env: VoidAuthEnv): string {
function resolveSecret(env: VoidAuthEnv, dev: boolean): string {
const explicit =
typeof env.BETTER_AUTH_SECRET === 'string' && env.BETTER_AUTH_SECRET.length > 0
? env.BETTER_AUTH_SECRET
Expand All @@ -99,8 +96,7 @@ function resolveSecret(request: Request, env: VoidAuthEnv): string {
return explicit;
}

const url = new URL(request.url);
if (isLocalHost(url.hostname)) {
if (dev) {
return 'void-dev-better-auth-secret-1234567890';
}

Expand Down Expand Up @@ -296,7 +292,7 @@ export function createVoidAuthRuntime(
const defaults: BetterAuthOptions = {
baseURL: origin,
basePath: '/api/auth',
secret: resolveSecret(request, env),
secret: resolveSecret(env, runtime.dev === true),
database,
...(emailAndPassword ? { emailAndPassword } : {}),
...(socialProviders ? { socialProviders } : {}),
Expand Down
21 changes: 18 additions & 3 deletions packages/void/test/unit/better-auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ describe('resolveVoidAuthOptions', () => {
).rejects.toThrow("auth: Missing credentials for provider 'google'");
});

it('requires BETTER_AUTH_SECRET outside localhost', async () => {
it('requires BETTER_AUTH_SECRET in production when none is set', async () => {
await expect(
resolveVoidAuthOptions(
new Request('https://example.com/api/auth'),
Expand All @@ -148,13 +148,28 @@ describe('resolveVoidAuthOptions', () => {
).rejects.toThrow('auth: Missing Better Auth secret. Set BETTER_AUTH_SECRET.');
});

it('uses the localhost fallback when BETTER_AUTH_SECRET is missing', async () => {
it('does not use the dev fallback secret for a spoofed localhost Host header in production', async () => {
await expect(
resolveVoidAuthOptions(
new Request('http://localhost:3000/api/auth'),
{},
{
dialect: 'sqlite',
providers: null,
},
{} as never,
),
).rejects.toThrow('auth: Missing Better Auth secret. Set BETTER_AUTH_SECRET.');
});

it('uses the dev fallback secret only when running in dev and BETTER_AUTH_SECRET is missing', async () => {
const { options } = await resolveVoidAuthOptions(
new Request('http://localhost:3000/api/auth'),
new Request('https://example.com/api/auth'),
{},
{
dialect: 'sqlite',
providers: null,
dev: true,
},
{} as never,
);
Expand Down
Loading