useSession returns null

Anyone knows why useSession returns null? 1) no cookie prefix or any cookie name customization 2) i can see cookie logs(in middleware)
export async function middleware(request: NextRequest) {
// In Next.js middleware, it's recommended to only check for the existence of a session cookie to handle redirection. To avoid blocking requests by making API or database calls.
const sessionCookie = getSessionCookie(request, {
});

logger.debug("🍪 Session cookie:", sessionCookie);
// etc....
export async function middleware(request: NextRequest) {
// In Next.js middleware, it's recommended to only check for the existence of a session cookie to handle redirection. To avoid blocking requests by making API or database calls.
const sessionCookie = getSessionCookie(request, {
});

logger.debug("🍪 Session cookie:", sessionCookie);
// etc....
3)but i get null from useSession
const { data: authClientSession } = authClient.useSession();
console.log(authClientSession) // returns null
const { data: authClientSession } = authClient.useSession();
console.log(authClientSession) // returns null
4) auth client looks like this
import {
adminClient,
emailOTPClient,
organizationClient,
} from "better-auth/client/plugins";
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
baseURL: "http://localhost:3000",
plugins: [emailOTPClient(), organizationClient(), adminClient()],
});
export const { signIn, signUp, useSession, signOut } = authClient;
import {
adminClient,
emailOTPClient,
organizationClient,
} from "better-auth/client/plugins";
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
baseURL: "http://localhost:3000",
plugins: [emailOTPClient(), organizationClient(), adminClient()],
});
export const { signIn, signUp, useSession, signOut } = authClient;
No description
Solution:
thanks! i realize that when you render something depdening on the session data and it's null at the begining, it just stops there and wont continue
Jump to solution
21 Replies
vz
vzOP3w ago
auth looks like this:
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
}),
plugins: [
admin(),
organization(),
nextCookies(),
emailOTP({
async sendVerificationOTP({ email, otp, type }) {
logger.info("Sending verification OTP to:", email);
logger.info(otp, "OTP:");
logger.info(type, "Type:");
},
}),
],
});
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
}),
plugins: [
admin(),
organization(),
nextCookies(),
emailOTP({
async sendVerificationOTP({ email, otp, type }) {
logger.info("Sending verification OTP to:", email);
logger.info(otp, "OTP:");
logger.info(type, "Type:");
},
}),
],
});
and it's latest version 1.2.5
Ping
Ping3w ago
Where are you using useSession at?
vz
vzOP3w ago
some tsx component with "use client";
Ping
Ping3w ago
Try moving nextCookies to the end of your plugins array.
vz
vzOP3w ago
still not working. the component looks like
"use client";

import....

export function NavUser() {
const router = useRouter();
const { isMobile } = useSidebar();
const { data: authClientSession } = authClient.useSession();
console.log(
"session from auth client in NavUser",
JSON.stringify(authClientSession, null, 2),
);


return (
....
);
}
"use client";

import....

export function NavUser() {
const router = useRouter();
const { isMobile } = useSidebar();
const { data: authClientSession } = authClient.useSession();
console.log(
"session from auth client in NavUser",
JSON.stringify(authClientSession, null, 2),
);


return (
....
);
}
Ping
Ping3w ago
Can you check what the error says if it exists? It's another option you can get out of useSession.
vz
vzOP3w ago
wait. i thought you are saying "remove". let me try one more time unfortunately no. i can see cookie is there from middleware loggings after signing in. GET /auth/login 200 in 971ms GET /api/auth/get-session 200 in 13ms 🍪 Session cookie: null 🍪 Session cookie: null session from auth server in LoginPage null GET /auth/login 200 in 75ms {"level":30,"time":1743794920050,"pid":64910,"hostname":"Weizhis-Mac-mini.local","msg":"Sending verification OTP to:"} POST /api/auth/email-otp/send-verification-otp 200 in 34ms {"level":30,"time":1743794920050,"pid":64910,"hostname":"Weizhis-Mac-mini.local","msg":"139991"} {"level":30,"time":1743794920050,"pid":64910,"hostname":"Weizhis-Mac-mini.local","msg":"sign-in"} POST /api/auth/sign-in/email-otp 200 in 39ms 🍪 Session cookie: Qu5xzZad3h3dVScTSuGRjvwxM8rlXH68.6ngMzfl7O9MP6zUNTlGGiYsUa8v8U%2FAn%2BOKibCqJYPI%3D GET /dashboard 200 in 31ms GET /api/auth/get-session 200 in 14ms GET /api/auth/get-session 200 in 17ms but not from useSession
session from auth client in NavUser null
session from auth client in NavUser null
i dont see any error tho
Ping
Ping3w ago
Can you set the entire result of useSession as 1 variable and log that variable? I want to check something.
vz
vzOP3w ago
it's already one variable. const { data: authClientSession } = authClient.useSession(); console.log( "session from auth client in NavUser", JSON.stringify(authClientSession, null, 2), and it's null
Ping
Ping3w ago
Don't destructure it. I mean to make the whole result of useSession 1 variable.
vz
vzOP3w ago
ah const data = authClient.useSession(); console.log( "data from auth client in NavUser", JSON.stringify(data, null, 2), );
data from auth client in NavUser {
"data": null,
"error": null,
"isPending": true,
"isRefetching": false
}
data from auth client in NavUser {
"data": null,
"error": null,
"isPending": true,
"isRefetching": false
}
Ping
Ping3w ago
Do you get any logs after that? Anytime it's pending the data will just be null
vz
vzOP3w ago
no
Ping
Ping3w ago
Can you check network panel in your devtools to see if the get-session requests resolves? And maybe also check if there is a response in that request.
Solution
vz
vz3w ago
thanks! i realize that when you render something depdening on the session data and it's null at the begining, it just stops there and wont continue
vz
vzOP3w ago
it's all working now. thank you @Ping
kenpachi
kenpachi2w ago
How did you manage to egt it working?
vick25
vick252d ago
Hello! I have a similar issue. The OTP is sent and the sign in works. The user and a session is created in the database but no cookies and the useSession is null. I'm using a server action to sign-in that looks like :
async function handleSubmit(formdata: FormData) {
'use server'
const code = formdata.get('code')
const email = loggedInEmail //props

// Sign in with emailed OTP
const { data, error } = await authClient.signIn.emailOtp({
email: (email as string).trim(),
otp: (code as string).trim()
})
if (error) {
console.error("Error verifying OTP:", error)
if (code === '')
redirect(`/historical`)
return
}
if (data.token) {
// console.log("OTP verified successfully")
redirect(`/historical?signup=success`)
}
redirect(`/historical`)
}
async function handleSubmit(formdata: FormData) {
'use server'
const code = formdata.get('code')
const email = loggedInEmail //props

// Sign in with emailed OTP
const { data, error } = await authClient.signIn.emailOtp({
email: (email as string).trim(),
otp: (code as string).trim()
})
if (error) {
console.error("Error verifying OTP:", error)
if (code === '')
redirect(`/historical`)
return
}
if (data.token) {
// console.log("OTP verified successfully")
redirect(`/historical?signup=success`)
}
redirect(`/historical`)
}
My auth.ts looks like :
export const auth = betterAuth({
database: prismaAdapter(Prisma, {
provider: "postgresql",
}),
advanced: {
useSecureCookies: process.env.NODE_ENV === "production",
},
session: {
expiresIn: 60 * 60 * 24 * 7,
updateAge: 60 * 60 * 24,
cookieCache: {
enabled: true,
maxAge: 5 * 60 // Cache duration in seconds
}
},
emailAndPassword: {
enabled: true,
// requireEmailVerification: true
},
plugins: [
emailOTP({
expiresIn: 600,
async sendVerificationOTP({ email, otp, type }) {
}),
openAPI(),
nextCookies(),
],
});
export type Session = typeof auth.$Infer.Session;
export const auth = betterAuth({
database: prismaAdapter(Prisma, {
provider: "postgresql",
}),
advanced: {
useSecureCookies: process.env.NODE_ENV === "production",
},
session: {
expiresIn: 60 * 60 * 24 * 7,
updateAge: 60 * 60 * 24,
cookieCache: {
enabled: true,
maxAge: 5 * 60 // Cache duration in seconds
}
},
emailAndPassword: {
enabled: true,
// requireEmailVerification: true
},
plugins: [
emailOTP({
expiresIn: 600,
async sendVerificationOTP({ email, otp, type }) {
}),
openAPI(),
nextCookies(),
],
});
export type Session = typeof auth.$Infer.Session;
And the authClient as :
export const authClient = createAuthClient({
baseURL: process.env.BETTER_AUTH_URL,
plugins: [
emailOTPClient()
]
});
export const authClient = createAuthClient({
baseURL: process.env.BETTER_AUTH_URL,
plugins: [
emailOTPClient()
]
});
Ping
Ping2d ago
You're using authClient in a server action. Any server related actions should use auth.api instead
vick25
vick252d ago
Thanks @Ping ! I'm working on it and will be back if it works @Ping 😆 Awesome. It works. You made my day. This is my implementation. Any refactoring?
async function handleSubmit(formdata: FormData) {
'use server'
const code = formdata.get('code')
const email = loggedInEmail

// Sign in with emailed OTP
const data = await auth.api.signInEmailOTP(
{
asResponse: true,
body: {
email: (email as string).trim(),
otp: (code as string).trim(),
},
headers: await headers(),
}
)
console.log(data)
if (!data) {
console.error("Error verifying OTP:")
if (code === '')
redirect(`/historical`)
return
}
if (data) {
// console.log("OTP verified successfully")
redirect(`/historical?signup=success`)
}
redirect(`/historical`)
}
async function handleSubmit(formdata: FormData) {
'use server'
const code = formdata.get('code')
const email = loggedInEmail

// Sign in with emailed OTP
const data = await auth.api.signInEmailOTP(
{
asResponse: true,
body: {
email: (email as string).trim(),
otp: (code as string).trim(),
},
headers: await headers(),
}
)
console.log(data)
if (!data) {
console.error("Error verifying OTP:")
if (code === '')
redirect(`/historical`)
return
}
if (data) {
// console.log("OTP verified successfully")
redirect(`/historical?signup=success`)
}
redirect(`/historical`)
}
Ping
Ping2d ago
Looks good to me 🫡

Did you find this page helpful?