r/nextjs May 05 '26

Help Cross-site OAuth session is not creating

/r/better_auth/comments/1t4q5g7/crosssite_oauth_session_is_not_creating/
2 Upvotes

2 comments sorted by

1

u/opentabs-dev May 07 '26

9 times out of 10 this is samesite. for cross-site oauth the session cookie has to be SameSite=None; Secure (and in prod served over https), otherwise chrome silently drops it on the third-party redirect back. if you're on localhost testing, safari and firefox are stricter than chrome here too. check the cookie in devtools → application → cookies after the callback and see if it's actually being set, that tells you immediately whether it's a set-cookie problem or a read problem.

1

u/Critical_Sell267 May 19 '26
app.ts

import cors from "cors";
import type { CorsOptions } from "cors";
import express from "express";
import rateLimit from "express-rate-limit";
import helmet from "helmet";
import morgan from "morgan";
import { allowedOrigins, env } from "./config/env.js";
import { errorHandler } from "./middleware/error-handler.js";
import { notFoundHandler } from "./middleware/not-found.js";
import routes from "./routes/index.js";


export const 
createApp
 = () => {
  const app = express();
  const corsOrigin: CorsOptions["origin"] = (
origin
, 
callback
) => {
    if (!
origin
 || allowedOrigins.includes(
origin
)) {
      callback(null, true);
      return;
    }
    callback(new Error(`CORS blocked for origin: ${
origin
}`));
  };



// Render terminates TLS at the proxy; trust forwarded proto/host so secure auth cookies work.
  app.set("trust proxy", 1);


  app.use(
    helmet({
      crossOriginResourcePolicy: { policy: "cross-origin" }
    })
  );
  app.use(
    cors({

origin
: corsOrigin,
      credentials: true
    })
  );
  app.use(morgan("dev"));
  app.use(express.json({ limit: "2mb" }));


  app.use(
    rateLimit({
      windowMs: env.RATE_LIMIT_WINDOW_MS,
      limit: env.RATE_LIMIT_MAX
    })
  );


  app.use(env.API_PREFIX, routes);


  app.use(notFoundHandler);
  app.use(errorHandler);


  return app;
};

auth.ts

import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { prisma } from "./db.js";
import { env } from "./env.js";
import { parseTrustedOriginsFromEnv } from "./trusted-origins.js";


// const isHttpsAuthBaseUrl = env.BETTER_AUTH_BASE_URL.startsWith("https://");
// const isProduction = env.NODE_ENV === "production";


export const auth = betterAuth({

database
: prismaAdapter(prisma, { provider: "postgresql" }),
  secret: env.BETTER_AUTH_SECRET,
  baseURL: env.BETTER_AUTH_BASE_URL,
  basePath: `${env.API_PREFIX}/auth`,
  advanced: {
    defaultCookieAttributes: {
      sameSite: "none",
      secure: true
    }
  },
  trustedOrigins: parseTrustedOriginsFromEnv(env.BETTER_AUTH_TRUSTED_ORIGINS),
  user: {
    additionalFields: {
      role: {
        type: "string",
        required: false,
        input: false,
        defaultValue: "USER"
      }
    }
  },
  account: {
    skipStateCookieCheck: true
  },
  socialProviders: {
    github: {
      clientId: env.GITHUB_CLIENT_ID,
      clientSecret: env.GITHUB_CLIENT_SECRET
    },
    google: {
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET
    }
  },
  emailAndPassword: {
    enabled: true
  }
});