-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Using secondaryStorage gives TypeError: expiresAt.getTime is not a function when calling auth.api.updateUser() #8156
Copy link
Copy link
Closed
Labels
bugSomething isn't workingSomething isn't workinglockedLocked conversations after being closed for 7 daysLocked conversations after being closed for 7 days
Description
Is this suited for github?
- Yes, this is suited for github
To Reproduce
I have the following setup:
apps/web/src/lib/auth.ts
import {betterAuth, BetterAuthOptions} from 'better-auth/minimal'
import {prismaAdapter} from 'better-auth/adapters/prisma'
import {customSession} from 'better-auth/plugins'
import {prisma} from 'database/src/client'
import {AUTH_COOKIE_PREFIX, DESKTOP_APP_CLIENT_ID, DESKTOP_APP_PROTOCOL, getURL, MAX_PASSWORD_CHARS, MAX_USERNAME_CHARS, MIN_PASSWORD_CHARS, MIN_USERNAME_CHARS} from 'utils'
import {nextCookies} from 'better-auth/next-js'
import {electron} from '@better-auth/electron'
import {waitUntil} from '@vercel/functions'
import {kv} from '@vercel/kv'
const options = {
appName: APP_NAME,
database: prismaAdapter(prisma, {
provider: 'postgresql',
usePlural: true
}),
baseURL: BASE_URL,
secondaryStorage: {
get: async (key) => await kv.get(`better-auth:${key}`),
set: async (key, value, ttl) => {
if (ttl) {
await kv.set(`better-auth:${key}`, value, {ex: ttl})
} else {
await kv.set(`better-auth:${key}`, value)
}
},
delete: async (key) => {
await kv.del(`better-auth:${key}`)
}
},
secret: process.env.BETTER_AUTH_SECRET!,
trustedOrigins: [
'http://localhost:3000',
'https://my-app.com',
],
socialProviders: {
google: {
prompt: 'select_account',
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
}
},
emailAndPassword: {
enabled: true,
},
advanced: {
cookiePrefix: AUTH_COOKIE_PREFIX,
useSecureCookies: process.env.NODE_ENV === 'production',
backgroundTasks: {
handler: (promise) => waitUntil(promise)
}
},
account: {
modelName: 'account',
encryptOAuthTokens: true,
accountLinking: {
enabled: true,
trustedProviders: [
'email-password',
'google',
'discord'
]
}
},
user: {
modelName: 'user',
additionalFields: {
bio: {
type: 'string',
required: false
},
backgroundImage: {
type: 'string',
required: false
},
status: {
type: 'string',
required: false
}
},
deleteUser: {
enabled: true
}
},
session: {
modelName: 'session',
expiresIn: 30 * 24 * 60 * 60, // 30 days
// https://www.better-auth.com/docs/guides/optimizing-for-performance#cookie-cache
cookieCache: {
enabled: true,
maxAge: 5 * 60 // Cache duration in seconds - 5 minutes
}
},
verification: {
modelName: 'verification'
},
rateLimit: {
enabled: true,
window: 60,
max: 100,
customRules: {
'/sign-in/email': {window: 60, max: 5},
'/sign-up/email': {window: 60, max: 5},
'/forget-password': {window: 60, max: 5}
}
},
...other code,
plugins: [
...other code,
]
} satisfies BetterAuthOptions
export const auth = betterAuth({
...options,
plugins: [
...(options.plugins ?? []),
customSession(({user, session}) => Promise.resolve({
user: {
...user,
...other code
},
session
}), options),
nextCookies() // make sure this is the last plugin in the array
]
})
and
apps/web/src/lib/auth-client.ts
import {customSessionClient, inferAdditionalFields, usernameClient} from 'better-auth/client/plugins'
import {createAuthClient} from 'better-auth/react'
import type {auth} from './auth'
import {AUTH_COOKIE_PREFIX, DESKTOP_APP_CLIENT_ID, DESKTOP_APP_PROTOCOL, getURL} from 'utils'
import {electronProxyClient} from '@better-auth/electron/proxy'
export const authClient = createAuthClient({
baseURL: getURL(),
plugins: [
electronProxyClient({
clientID: DESKTOP_APP_CLIENT_ID,
cookiePrefix: AUTH_COOKIE_PREFIX,
protocol: {
scheme: DESKTOP_APP_PROTOCOL
}
}),
usernameClient(),
customSessionClient<typeof auth>(),
inferAdditionalFields<typeof auth>()
],
fetchOptions: {
onError(e) {
if (e.error.status === 429) {
toastIt({text: 'Too many requests. Please try again later.', type: ToastType.Error})
}
}
}
})
Current vs. Expected behavior
When I have secondaryStorage enabled (i.e. not commented out), when I update the user on the server with:
await auth.api.updateUser({
body: {
status: 'pending',
},
headers: await headers()
})
I get:
TypeError: expiresAt.getTime is not a function
at async POST (src/app/api/user/update-user-profile/route.ts:63:5)
61 | // Use auth.api.updateUser to update user and session together.
62 | // This ensures the session reflects the updated values immediately.
> 63 | await auth.api.updateUser({
| ^
64 | body: {
65 | status: 'pending',
What version of Better Auth are you using?
1.5.0-beta.19
System info
{
"system": {
"platform": "darwin",
"arch": "arm64",
"version": "Darwin",
"release": "25.3.0",
"cpuCount": 12,
"cpuModel": "Apple M2 Pro",
"totalMemory": "32.00 GB",
"freeMemory": "3.64 GB"
},
"node": {
"version": "v21.7.3",
"env": "development"
},
"packageManager": {
"name": "npm",
"version": "10.5.0"
},
"frameworks": [
{
"name": "next",
"version": "^15.5.9"
},
{
"name": "react",
"version": "^19.2.1"
}
],
"databases": null,
"betterAuth": {
"version": "Unknown",
"config": null,
"error": "Missing API key. Pass it to the constructor `new Resend(\"re_123\")`"
}
}Which area(s) are affected? (Select all that apply)
Backend
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workinglockedLocked conversations after being closed for 7 daysLocked conversations after being closed for 7 days