Skip to content
Merged
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
6 changes: 6 additions & 0 deletions __mocks__/better-auth-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ export const emailOTP = () => ({
rateLimit: [],
options: {},
});

export const oneTap = (options = {}) => ({
id: 'one-tap',
endpoints: {},
options,
});
72 changes: 72 additions & 0 deletions __tests__/routes/betterAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,44 @@ describe('betterAuth routes', () => {
});
});

it('should register the one-tap plugin when GOOGLE_CLIENT_ID is set', async () => {
const original = process.env.GOOGLE_CLIENT_ID;
process.env.GOOGLE_CLIENT_ID = 'test-google-client-id';
try {
const { getBetterAuthOptions } = await import('../../src/betterAuth');
const options = getBetterAuthOptions(
(con.driver as unknown as { master: Pool }).master,
);
expect(options.plugins?.some((plugin) => plugin.id === 'one-tap')).toBe(
true,
);
} finally {
if (original === undefined) {
delete process.env.GOOGLE_CLIENT_ID;
} else {
process.env.GOOGLE_CLIENT_ID = original;
}
}
});

it('should not register the one-tap plugin without GOOGLE_CLIENT_ID', async () => {
const original = process.env.GOOGLE_CLIENT_ID;
delete process.env.GOOGLE_CLIENT_ID;
try {
const { getBetterAuthOptions } = await import('../../src/betterAuth');
const options = getBetterAuthOptions(
(con.driver as unknown as { master: Pool }).master,
);
expect(options.plugins?.some((plugin) => plugin.id === 'one-tap')).toBe(
false,
);
} finally {
if (original !== undefined) {
process.env.GOOGLE_CLIENT_ID = original;
}
}
});

describe('user.create.before hook', () => {
const getBeforeHook = async () => {
const { getBetterAuthOptions } = await import('../../src/betterAuth');
Expand Down Expand Up @@ -238,6 +276,7 @@ describe('betterAuth routes', () => {
body?: Record<string, unknown>;
cookie?: string;
ip?: string;
timezone?: string;
} = {},
) => {
const headers: Record<string, string> = {};
Expand All @@ -247,6 +286,9 @@ describe('betterAuth routes', () => {
if (overrides.ip) {
headers['x-forwarded-for'] = overrides.ip;
}
if (overrides.timezone) {
headers['x-timezone'] = overrides.timezone;
}
return {
request: new Request('http://localhost/auth/sign-up/email', {
headers: Object.keys(headers).length ? headers : undefined,
Expand Down Expand Up @@ -340,6 +382,36 @@ describe('betterAuth routes', () => {
);
});

it('should set timezone from the x-timezone header', async () => {
const after = await getAfterHook();
const user = await createBaseUser();

await after(user, makeContext({ timezone: 'Europe/Zagreb' }));

const persisted = await con
.getRepository(User)
.findOneBy({ id: user.id });
expect(persisted!.timezone).toBe('Europe/Zagreb');
});

it('should prefer body timezone over the x-timezone header', async () => {
const after = await getAfterHook();
const user = await createBaseUser();

await after(
user,
makeContext({
body: { timezone: 'America/New_York' },
timezone: 'Europe/Zagreb',
}),
);

const persisted = await con
.getRepository(User)
.findOneBy({ id: user.id });
expect(persisted!.timezone).toBe('America/New_York');
});

it('should claim opportunities that user created as anonymous', async () => {
const after = await getAfterHook();
const user = await createBaseUser();
Expand Down
13 changes: 11 additions & 2 deletions src/betterAuth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { betterAuth, type BetterAuthOptions } from 'better-auth';
import { APIError, createAuthMiddleware, getOAuthState } from 'better-auth/api';
import { captcha, emailOTP } from 'better-auth/plugins';
import { captcha, emailOTP, oneTap } from 'better-auth/plugins';
import type { Pool } from 'pg';
import * as argon2 from 'argon2';
import * as bcryptjs from 'bcryptjs';
Expand Down Expand Up @@ -597,7 +597,13 @@ export const getBetterAuthOptions = (pool: Pool): BetterAuthOptions => {
'referralOrigin',
body?.referralOrigin ?? cookieReferral?.referralOrigin,
);
addField('timezone', body?.timezone ?? oauthState?.timezone);
addField(
'timezone',
body?.timezone ??
oauthState?.timezone ??
// used primarily in one-tap flow
hookCtx?.request?.headers?.get('x-timezone'),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

could not pass in body from onetap payload

);

if (typeof body?.acceptedMarketing === 'boolean') {
setClauses.push(`"acceptedMarketing" = $${paramIndex}`);
Expand Down Expand Up @@ -772,6 +778,9 @@ export const getBetterAuthOptions = (pool: Pool): BetterAuthOptions => {
});
},
}),
...(process.env.GOOGLE_CLIENT_ID
? [oneTap({ clientId: process.env.GOOGLE_CLIENT_ID })]
: []),
],
socialProviders: {
...(process.env.GOOGLE_CLIENT_ID && {
Expand Down
Loading