nktnet
nktnet
BABetter Auth
Created by shubhattin on 3/4/2025 in #help
Google Auth Not working in Production
I use window.location.origin as well for separate frontend and backend. For CORs issues, it's probably related to your trustedOrigin configuration in the auth instance. Are you able to share this? Docs here: - https://www.better-auth.com/docs/concepts/cookies#cross-subdomain-cookies
import { betterAuth } from "better-auth"

export const auth = betterAuth({
advanced: {
crossSubDomainCookies: {
enabled: true,
domain: ".example.com", // Domain with a leading period
},
defaultCookieAttributes: {
secure: true,
httpOnly: true,
sameSite: "none", // Allows CORS-based cookie sharing across subdomains
partitioned: true, // New browser standards will mandate this for foreign cookies
},
},
trustedOrigins: [
'https://example.com',
'https://app1.example.com',
'https://app2.example.com',
],
})
import { betterAuth } from "better-auth"

export const auth = betterAuth({
advanced: {
crossSubDomainCookies: {
enabled: true,
domain: ".example.com", // Domain with a leading period
},
defaultCookieAttributes: {
secure: true,
httpOnly: true,
sameSite: "none", // Allows CORS-based cookie sharing across subdomains
partitioned: true, // New browser standards will mandate this for foreign cookies
},
},
trustedOrigins: [
'https://example.com',
'https://app1.example.com',
'https://app2.example.com',
],
})
The other thing to note is there may be CORs set on the backend server itself, separate from better-auth. For example, in hono, you need to also configure: - https://www.better-auth.com/docs/integrations/hono#cors
import { Hono } from "hono";
import { auth } from "./auth";
import { serve } from "@hono/node-server";
import { cors } from "hono/cors";

const app = new Hono();

app.use(
"/api/auth/*", // or replace with "*" to enable cors for all routes
cors({
origin: "http://localhost:3001", // replace with your origin
allowHeaders: ["Content-Type", "Authorization"],
allowMethods: ["POST", "GET", "OPTIONS"],
exposeHeaders: ["Content-Length"],
maxAge: 600,
credentials: true,
}),
);
import { Hono } from "hono";
import { auth } from "./auth";
import { serve } from "@hono/node-server";
import { cors } from "hono/cors";

const app = new Hono();

app.use(
"/api/auth/*", // or replace with "*" to enable cors for all routes
cors({
origin: "http://localhost:3001", // replace with your origin
allowHeaders: ["Content-Type", "Authorization"],
allowMethods: ["POST", "GET", "OPTIONS"],
exposeHeaders: ["Content-Length"],
maxAge: 600,
credentials: true,
}),
);
Finally, if you're using Safari, you may want to check on another browser first (e.g. Chrome/Firefox) to see if the issue can be reproduced. Can't really say much else without more details into your configurations.
9 replies
BABetter Auth
Created by varo on 3/9/2025 in #help
betterauth/react acting weird and hono cors problem
@varo your issue is probably the origin array in your first image For example, you have a single string:
origin: ["http://localhost:5173, http://localhost:3000"]
origin: ["http://localhost:5173, http://localhost:3000"]
when it should be separate strings (not stuck together in the same quote):
origin: ["http://localhost:5173", "http://localhost:3000"]
origin: ["http://localhost:5173", "http://localhost:3000"]
Also, there's no reason to specify the backend in trustedOrigin, so really you should just use
origin: ["http://localhost:5173"]
origin: ["http://localhost:5173"]
Cors work fine for me, same setup with - React frontend (vite, tanstack router) - Hono backend (nodejs) Full repo here for your reference: https://github.com/nktnet1/rt-stack
6 replies
BABetter Auth
Created by nktnet on 1/17/2025 in #help
[Solved] use google auth to access other google apis (e.g. Calendar/Drive)
Solved - you will need to query the account table, where your accessToken and refreshToken resides. Example (I'm using the drizzle-orm adapter):
import { google } from 'googleapis';
import { headers } from 'next/headers';
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '@/config/server';
import { db } from '@/db/db';
import { auth } from '@/lib/auth';

export default async function CalendarList() {
const session = await auth.api.getSession({
headers: await headers(),
});

if (!session?.user) {
return <div>Not logged in!</div>;
}

const googleAuth = new google.auth.OAuth2({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
});

const account = await db.query.accountsTable.findFirst({
where: (table, { and, eq }) =>
and(eq(table.userId, session.user.id), eq(table.providerId, 'google')),
});

if (!account) {
return <div>Please also log in with Google at least once!</div>;
}

googleAuth.setCredentials({
access_token: account.accessToken,
refresh_token: account.refreshToken,
});

const calendar = google.calendar({ version: 'v3', auth: googleAuth });
const calendarList = await calendar.calendarList.list();
const writableCalendarList = calendarList.data.items?.filter(
(cal) => cal.accessRole && ['owner', 'writer'].includes(cal.accessRole),
);

return (
<div>
<div className="whitespace-pre-wrap">You are: {JSON.stringify(session, null, 2)}</div>
<div className="whitespace-pre-wrap">
Writable Calendars: {JSON.stringify(writableCalendarList, null, 2)}
</div>
</div>
);
}
import { google } from 'googleapis';
import { headers } from 'next/headers';
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '@/config/server';
import { db } from '@/db/db';
import { auth } from '@/lib/auth';

export default async function CalendarList() {
const session = await auth.api.getSession({
headers: await headers(),
});

if (!session?.user) {
return <div>Not logged in!</div>;
}

const googleAuth = new google.auth.OAuth2({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
});

const account = await db.query.accountsTable.findFirst({
where: (table, { and, eq }) =>
and(eq(table.userId, session.user.id), eq(table.providerId, 'google')),
});

if (!account) {
return <div>Please also log in with Google at least once!</div>;
}

googleAuth.setCredentials({
access_token: account.accessToken,
refresh_token: account.refreshToken,
});

const calendar = google.calendar({ version: 'v3', auth: googleAuth });
const calendarList = await calendar.calendarList.list();
const writableCalendarList = calendarList.data.items?.filter(
(cal) => cal.accessRole && ['owner', 'writer'].includes(cal.accessRole),
);

return (
<div>
<div className="whitespace-pre-wrap">You are: {JSON.stringify(session, null, 2)}</div>
<div className="whitespace-pre-wrap">
Writable Calendars: {JSON.stringify(writableCalendarList, null, 2)}
</div>
</div>
);
}
2 replies
BABetter Auth
Created by nktnet on 12/31/2024 in #help
Typescript - how to infer additional, non-database fields during registration
The issue is, registrationToken is not a user field to be stored in the database and won't be inferred - it's a variable that the frontend submits to the backend, solely for the purpose of comparing against the backend's REGISTRATION_TOKEN variable (similar to how a matrix server would require one to register)
4 replies
BABetter Auth
Created by nktnet on 12/31/2024 in #help
Typescript - how to infer additional, non-database fields during registration
After some investigation, I discovered that the following hack/workaround can be used to remove the type error:
export const { signIn, signUp, signOut, useSession } = createAuthClient({
baseURL: process.env.BETTER_AUTH_URL ?? 'http://localhost:3000',
plugins: [
inferAdditionalFields({
user: {
registrationToken: {
type: 'string',
},
},
}),
],
});
export const { signIn, signUp, signOut, useSession } = createAuthClient({
baseURL: process.env.BETTER_AUTH_URL ?? 'http://localhost:3000',
plugins: [
inferAdditionalFields({
user: {
registrationToken: {
type: 'string',
},
},
}),
],
});
Note: if we instead follow the docs and use
import { inferAdditionalFields } from 'better-auth/client/plugins';
import { createAuthClient } from 'better-auth/react';
import type { auth } from '@/lib/auth';

export const { signIn, signUp, signOut, useSession } = createAuthClient({
baseURL: process.env.BETTER_AUTH_URL ?? 'http://localhost:3000',
plugins: [
inferAdditionalFields<typeof auth>({
user: {
registrationToken: {
type: 'string',
},
},
}),
],
});
import { inferAdditionalFields } from 'better-auth/client/plugins';
import { createAuthClient } from 'better-auth/react';
import type { auth } from '@/lib/auth';

export const { signIn, signUp, signOut, useSession } = createAuthClient({
baseURL: process.env.BETTER_AUTH_URL ?? 'http://localhost:3000',
plugins: [
inferAdditionalFields<typeof auth>({
user: {
registrationToken: {
type: 'string',
},
},
}),
],
});
to try to sync with the server auth types, the hack for registrationToken will no longer work (it gets ignored). Is there a better way? Is there a better way?
4 replies