CORS Issue with Access-Control-Allow-Credentials (true,true) in Better Auth
Issue Description
I am encountering a persistent CORS policy issue when integrating Better Auth in my project. The browser blocks requests due to an invalid Access-Control-Allow-Credentials header value (true,true). Here’s the error message from the browser console:
Access to fetch at 'http://localhost:3000/prod/v1/auth/sign-in/social?currentURL=http%3A%2F%2Flocalhost%3A3001%2F' from origin 'http://localhost:3001' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is 'true,true' which must be 'true' when the request's credentials mode is 'include'.
Access to fetch at 'http://localhost:3000/prod/v1/auth/sign-in/social?currentURL=http%3A%2F%2Flocalhost%3A3001%2F' from origin 'http://localhost:3001' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is 'true,true' which must be 'true' when the request's credentials mode is 'include'.

4 Replies
Server Code
Better Auth Configuration
Client Code
Auth Client
Current Behavior
Despite all these fixes, the browser continues to report:
Request for Help
- How can I ensure the Access-Control-Allow-Credentials header is set correctly in this setup?
- Are there any known issues with Better Auth or Serverless & Hono's integration in this context?
Any guidance or suggestions would be greatly appreciated! 🙏
Hey @bekacru If u already know about this, can guide me?
import { Hono } from "hono";
import { handle } from "hono/aws-lambda";
import { cors } from "hono/cors";
import { auth } from "@/lib/auth"; // Better Auth configuration
const app = new Hono();
// CORS middleware configuration
app.use(
cors({
origin: ["http://localhost:3001", "http://localhost:3000"], // Allowed origins
allowHeaders: ["Content-Type", "Authorization"], // Allowed headers
allowMethods: ["POST", "GET", "OPTIONS"], // Allowed HTTP methods
exposeHeaders: ["Content-Length"], // Headers exposed to the client
maxAge: 600, // Cache duration for CORS preflight
credentials: true, // Enables sending credentials
})
);
// Middleware to log requests and responses
app.use("*", async (c, next) => {
console.log(`Request: ${c.req.method} ${c.req.url}`);
await next();
console.log("Response Headers:", c.res.headers);
});
// Auth route
app.on(["POST", "GET"], "/prod/v1/auth/**", async (c) => {
console.log("Handler invoked for:", c.req.url);
try {
// Forward request to Better Auth handler
return await auth.handler(c.req.raw);
} catch (error) {
console.error("Error in /prod/v1/auth/** route:", error);
return c.json(
{
message: "An unexpected error occurred.",
error: error instanceof Error ? error.message : "Unknown error",
},
500
);
}
});
// Root endpoint for testing
app.get("/", (c) => {
return c.text("The Hono Server is running...!");
});
// Global error handler
app.onError((err, c) => {
console.error("Global error handler caught:", err);
return c.json(
{
message: "A global error occurred.",
error: err instanceof Error ? err.message : "Unknown error",
},
500
);
});
// AWS Lambda handler
export const handler = handle(app);
import { Hono } from "hono";
import { handle } from "hono/aws-lambda";
import { cors } from "hono/cors";
import { auth } from "@/lib/auth"; // Better Auth configuration
const app = new Hono();
// CORS middleware configuration
app.use(
cors({
origin: ["http://localhost:3001", "http://localhost:3000"], // Allowed origins
allowHeaders: ["Content-Type", "Authorization"], // Allowed headers
allowMethods: ["POST", "GET", "OPTIONS"], // Allowed HTTP methods
exposeHeaders: ["Content-Length"], // Headers exposed to the client
maxAge: 600, // Cache duration for CORS preflight
credentials: true, // Enables sending credentials
})
);
// Middleware to log requests and responses
app.use("*", async (c, next) => {
console.log(`Request: ${c.req.method} ${c.req.url}`);
await next();
console.log("Response Headers:", c.res.headers);
});
// Auth route
app.on(["POST", "GET"], "/prod/v1/auth/**", async (c) => {
console.log("Handler invoked for:", c.req.url);
try {
// Forward request to Better Auth handler
return await auth.handler(c.req.raw);
} catch (error) {
console.error("Error in /prod/v1/auth/** route:", error);
return c.json(
{
message: "An unexpected error occurred.",
error: error instanceof Error ? error.message : "Unknown error",
},
500
);
}
});
// Root endpoint for testing
app.get("/", (c) => {
return c.text("The Hono Server is running...!");
});
// Global error handler
app.onError((err, c) => {
console.error("Global error handler caught:", err);
return c.json(
{
message: "A global error occurred.",
error: err instanceof Error ? err.message : "Unknown error",
},
500
);
});
// AWS Lambda handler
export const handler = handle(app);
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/db"; // Replace with your actual DB connection
import { env } from "@/config/env"; // Environment loader
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
}),
baseURL: env.BETTER_AUTH_URL || "http://localhost:3000/prod/v1/auth",
secret: env.BETTER_AUTH_SECRET,
emailAndPassword: {
enabled: true,
autoSignIn: true,
minPasswordLength: 8,
},
socialProviders: {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
},
},
});
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/db"; // Replace with your actual DB connection
import { env } from "@/config/env"; // Environment loader
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
}),
baseURL: env.BETTER_AUTH_URL || "http://localhost:3000/prod/v1/auth",
secret: env.BETTER_AUTH_SECRET,
emailAndPassword: {
enabled: true,
autoSignIn: true,
minPasswordLength: 8,
},
socialProviders: {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
},
},
});
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: "http://localhost:3000/prod/v1/api/auth",
disableCSRFTokenCheck: true, // Disable CSRF for development
});
React Component
"use client";
import { authClient } from "@/lib/auth-client";
export default function Home() {
const signInWithGoogle = async () => {
try {
const result = await authClient.signIn.social({
provider: "google",
callbackURL: "http://localhost:3001",
});
if (result.error) {
console.error("Error signing in with Google:", result.error);
alert("Failed to sign in with Google.");
return;
}
console.log("Sign-in successful:", result);
alert("Sign-in successful!");
} catch (error) {
console.error("Unexpected error during Google sign-in:", error);
alert("An unexpected error occurred. Please try again.");
}
};
return (
<main className="flex flex-col items-center justify-center h-screen">
<h1 className="text-2xl font-bold mb-4">Welcome to Better Auth</h1>
<button
onClick={signInWithGoogle}
>
Sign in with Google
</button>
</main>
);
}
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: "http://localhost:3000/prod/v1/api/auth",
disableCSRFTokenCheck: true, // Disable CSRF for development
});
React Component
"use client";
import { authClient } from "@/lib/auth-client";
export default function Home() {
const signInWithGoogle = async () => {
try {
const result = await authClient.signIn.social({
provider: "google",
callbackURL: "http://localhost:3001",
});
if (result.error) {
console.error("Error signing in with Google:", result.error);
alert("Failed to sign in with Google.");
return;
}
console.log("Sign-in successful:", result);
alert("Sign-in successful!");
} catch (error) {
console.error("Unexpected error during Google sign-in:", error);
alert("An unexpected error occurred. Please try again.");
}
};
return (
<main className="flex flex-col items-center justify-center h-screen">
<h1 className="text-2xl font-bold mb-4">Welcome to Better Auth</h1>
<button
onClick={signInWithGoogle}
>
Sign in with Google
</button>
</main>
);
}
Access to fetch at 'http://localhost:3000/prod/v1/auth/sign-in/social?currentURL=http%3A%2F%2Flocalhost%3A3001%2F' from origin 'http://localhost:3001' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is 'true,true' which must be 'true' when the request's credentials mode is 'include'.
Access to fetch at 'http://localhost:3000/prod/v1/auth/sign-in/social?currentURL=http%3A%2F%2Flocalhost%3A3001%2F' from origin 'http://localhost:3001' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is 'true,true' which must be 'true' when the request's credentials mode is 'include'.
ANY /prod/v1/api/auth/sign-in/social (λ: app)
Unmatched route: https://offlinecontext_domainname/v1/api/auth/sign-in/social?currentURL=http://localhost:3001/
(λ: app) RequestId: cdb897e2-310e-4220-91a4-625554b24678 Duration: 220.65 ms Billed Duration: 221 ms
Handler invoked for: https://offlinecontext_domainname/v1/api/auth/sign-in/social?currentURL=http://localhost:3001/
ANY /prod/v1/api/auth/sign-in/social (λ: app)
Unmatched route: https://offlinecontext_domainname/v1/api/auth/sign-in/social?currentURL=http://localhost:3001/
(λ: app) RequestId: cdb897e2-310e-4220-91a4-625554b24678 Duration: 220.65 ms Billed Duration: 221 ms
Handler invoked for: https://offlinecontext_domainname/v1/api/auth/sign-in/social?currentURL=http://localhost:3001/
not sure what's the issue. But it's not directly related to better auth but something isn't right with your hono cors config. But by the looks of it, it looks like it should work.
Yeah, this same code without any changes works in Deno (a normal Node.js server might also work with this code, but I haven't tried it yet).
However, it gives this error only with AWS Lambda serverless.
Unknown User•6mo ago
Message Not Public
Sign In & Join Server To View