[GH-ISSUE #2196] auth.api.getSession Returns null for Custom Telegram Plugin Despite Cookie Being Set #9096

Closed
opened 2026-04-13 04:24:29 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @bkyerv on GitHub (Apr 9, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/2196

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

I’m building a custom Telegram authentication plugin for better-auth in an Astro project, but I’m encountering an issue where auth.api.getSession returns null even though the better_auth.session_token cookie is set in the browser. The Google authentication flow works perfectly, but the Telegram flow fails to maintain the session after redirecting to a protected route.

The user and account data are correctly stored in the database (verified via database queries), and the cookie is set with proper attributes (e.g., maxAge, path, etc.). However, subsequent requests to protected routes show Request cookies: null and Session: null, indicating the session isn’t recognized.

Current vs. Expected behavior

Expected Behavior

  • After Telegram authentication, the better_auth.session_token cookie should be set in the browser.
  • Subsequent requests to protected routes (e.g., /protected/images/results) should include the cookie in the request headers.
  • auth.api.getSession should return the session and user data, allowing access to protected routes.

Actual Behavior

  • The cookie is set in the browser (visible in Dev Tools > Application > Cookies).
  • However, subsequent requests show Request cookies: null and Session: null in the logs.
  • The middleware redirects to /signin because the session isn’t recognized.

What version of Better Auth are you using?

1.2.5

Provide environment information

Both on macos and linux
Chrome
Node.js version: (e.g., v20.12.1)
Framework: Astro
Database: SQLite with drizzle-orm

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { userDb, userDbClient } from "../db/connection";
import { account, session, user, verification } from "src/db/auth-schema";
import { telegramPlugin } from "src/plugins/telegram-server-plugin";

export const auth = betterAuth({
  advanced: {
    cookiePrefix: "better_auth",
  },
  database: drizzleAdapter(userDb, {
    provider: "sqlite",
    schema: {
      verification,
      user,
      account,
      session,
    },
  }),
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    },
  },
  user: {
    additionalFields: {
      role: {
        type: "string",
        required: true,
        defaultValue: "regular_user",
        input: false,
      },
    },
  },
  plugins: [
    telegramPlugin({
      botToken: process.env.TELEGRAM_BOT_TOKEN!,
    }),
  ],
});

Additional context

telegram plugin - https://gist.github.com/bkyerv/a73ea2c49cc89c6261e19ff07004dd6d

middleware code

import { defineMiddleware } from "astro:middleware";
import { auth } from "./lib/auth";

export const onRequest = defineMiddleware(async (context, next) => {
  // console.log("from middleware headers", context.request.headers);
  const isAuthed = await auth.api.getSession({
    headers: context.request.headers,
  });

  console.log("Request cookies:", context.request.headers.get("cookie"));
  console.log(
    "Session:",
    await auth.api.getSession({ headers: context.request.headers })
  );
  // console.log("from middleware", isAuthed);
  if (context.url.pathname.startsWith("/protected") && !isAuthed) {
    return context.redirect("/signin");
  }
  if (context.url.pathname.startsWith("/signin") && isAuthed) {
    return context.redirect("/protected/images");
  }

  if (isAuthed) {
    context.locals.user = isAuthed.user;
    context.locals.session = isAuthed.session;
  }

  return next();
});

logs

Found existing user yo yo yo (e4065668-d60a-4590-b858-071703e4535d) via Telegram account.
Cookie attributes: {
  secure: false,
  sameSite: 'lax',
  path: '/',
  httpOnly: true,
  maxAge: 2592000
}
Signed cookie set with name: better_auth.session_token
Session token: 0gc3b5YWzrcCDgsoHROc42y1DXecB0N5
Request cookies: __Secure-better_auth.session_token=0gc3b5YWzrcCDgsoHROc42y1DXecB0N5
Request cookies: __Secure-better_auth.session_token=0gc3b5YWzrcCDgsoHROc42y1DXecB0N5
Session: null
Bot username: lense_472_bot

Additional Context

  • The Google authentication flow works perfectly, setting the same better_auth.session_token cookie and recognizing the session.
  • The user and account data are correctly stored in the database (verified via database queries).
  • I’ve tried using setSignedCookie as recommended, but the issue persists.
  • I’m running the app locally with ngrok to expose it for Telegram’s callback (using HTTP, so secure: false).

Questions
Why does auth.api.getSession return null even though the cookie is set and the session token exists in the database?

Originally created by @bkyerv on GitHub (Apr 9, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/2196 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce I’m building a custom Telegram authentication plugin for better-auth in an Astro project, but I’m encountering an issue where auth.api.getSession returns null even though the better_auth.session_token cookie is set in the browser. The Google authentication flow works perfectly, but the Telegram flow fails to maintain the session after redirecting to a protected route. The user and account data are correctly stored in the database (verified via database queries), and the cookie is set with proper attributes (e.g., maxAge, path, etc.). However, subsequent requests to protected routes show Request cookies: null and Session: null, indicating the session isn’t recognized. ### Current vs. Expected behavior **Expected Behavior** - After Telegram authentication, the better_auth.session_token cookie should be set in the browser. - Subsequent requests to protected routes (e.g., /protected/images/results) should include the cookie in the request headers. - auth.api.getSession should return the session and user data, allowing access to protected routes. **Actual Behavior** - The cookie is set in the browser (visible in Dev Tools > Application > Cookies). - However, subsequent requests show Request cookies: null and Session: null in the logs. - The middleware redirects to /signin because the session isn’t recognized. ### What version of Better Auth are you using? 1.2.5 ### Provide environment information ```bash Both on macos and linux Chrome Node.js version: (e.g., v20.12.1) Framework: Astro Database: SQLite with drizzle-orm ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { userDb, userDbClient } from "../db/connection"; import { account, session, user, verification } from "src/db/auth-schema"; import { telegramPlugin } from "src/plugins/telegram-server-plugin"; export const auth = betterAuth({ advanced: { cookiePrefix: "better_auth", }, database: drizzleAdapter(userDb, { provider: "sqlite", schema: { verification, user, account, session, }, }), socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }, }, user: { additionalFields: { role: { type: "string", required: true, defaultValue: "regular_user", input: false, }, }, }, plugins: [ telegramPlugin({ botToken: process.env.TELEGRAM_BOT_TOKEN!, }), ], }); ``` ### Additional context telegram plugin - https://gist.github.com/bkyerv/a73ea2c49cc89c6261e19ff07004dd6d middleware code ``` import { defineMiddleware } from "astro:middleware"; import { auth } from "./lib/auth"; export const onRequest = defineMiddleware(async (context, next) => { // console.log("from middleware headers", context.request.headers); const isAuthed = await auth.api.getSession({ headers: context.request.headers, }); console.log("Request cookies:", context.request.headers.get("cookie")); console.log( "Session:", await auth.api.getSession({ headers: context.request.headers }) ); // console.log("from middleware", isAuthed); if (context.url.pathname.startsWith("/protected") && !isAuthed) { return context.redirect("/signin"); } if (context.url.pathname.startsWith("/signin") && isAuthed) { return context.redirect("/protected/images"); } if (isAuthed) { context.locals.user = isAuthed.user; context.locals.session = isAuthed.session; } return next(); }); ``` logs ``` Found existing user yo yo yo (e4065668-d60a-4590-b858-071703e4535d) via Telegram account. Cookie attributes: { secure: false, sameSite: 'lax', path: '/', httpOnly: true, maxAge: 2592000 } Signed cookie set with name: better_auth.session_token Session token: 0gc3b5YWzrcCDgsoHROc42y1DXecB0N5 Request cookies: __Secure-better_auth.session_token=0gc3b5YWzrcCDgsoHROc42y1DXecB0N5 Request cookies: __Secure-better_auth.session_token=0gc3b5YWzrcCDgsoHROc42y1DXecB0N5 Session: null Bot username: lense_472_bot ``` **Additional Context** - The Google authentication flow works perfectly, setting the same better_auth.session_token cookie and recognizing the session. - The user and account data are correctly stored in the database (verified via database queries). - I’ve tried using setSignedCookie as recommended, but the issue persists. - I’m running the app locally with ngrok to expose it for Telegram’s callback (using HTTP, so secure: false). **Questions** Why does auth.api.getSession return null even though the cookie is set and the session token exists in the database?
GiteaMirror added the locked label 2026-04-13 04:24:29 -05:00
Author
Owner

@Kinfe123 commented on GitHub (Apr 10, 2025):

one thing i could see is - the cookie is being set with __Secure- prefix but with secure: false, which is causing the mismatch.

<!-- gh-comment-id:2791778231 --> @Kinfe123 commented on GitHub (Apr 10, 2025): one thing i could see is - the cookie is being set with __Secure- prefix but with secure: false, which is causing the mismatch.
Author
Owner

@1bye commented on GitHub (Apr 17, 2025):

Same issue

<!-- gh-comment-id:2812503743 --> @1bye commented on GitHub (Apr 17, 2025): Same issue
Author
Owner

@1bye commented on GitHub (Apr 17, 2025):

@bkyerv Have you fixed?

<!-- gh-comment-id:2812504300 --> @1bye commented on GitHub (Apr 17, 2025): @bkyerv Have you fixed?
Author
Owner

@1bye commented on GitHub (Apr 17, 2025):

Setting cookie as signed works

await setSignedCookie(
    cookie.name,
    session.token,
    process.env.BETTER_AUTH_SECRET!,
    cookie.attributes
);
<!-- gh-comment-id:2812556439 --> @1bye commented on GitHub (Apr 17, 2025): Setting cookie as signed works ```ts await setSignedCookie( cookie.name, session.token, process.env.BETTER_AUTH_SECRET!, cookie.attributes ); ```
Author
Owner

@bkyerv commented on GitHub (Apr 17, 2025):

@1bye can you please share your implementation of the plugin and how you integrated it?

<!-- gh-comment-id:2813532071 --> @bkyerv commented on GitHub (Apr 17, 2025): @1bye can you please share your implementation of the plugin and how you integrated it?
Author
Owner

@dannylin108 commented on GitHub (Apr 28, 2025):

@1bye can you please share your implementation of the plugin and how you integrated it?

Your code should use

await setSignedCookie(
    cookie.name,
    session.token,
    process.env.BETTER_AUTH_SECRET!,
    cookie.attributes
);

instead of setCookie. await is important here ;-)

<!-- gh-comment-id:2833975672 --> @dannylin108 commented on GitHub (Apr 28, 2025): > [@1bye](https://github.com/1bye) can you please share your implementation of the plugin and how you integrated it? Your code should use ```TS await setSignedCookie( cookie.name, session.token, process.env.BETTER_AUTH_SECRET!, cookie.attributes ); ``` instead of `setCookie`. `await` is important here ;-)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#9096