sebastian
sebastian
BABetter Auth
Created by sebastian on 5/11/2025 in #help
Weird behavior when rememberMe is false.
When logging in with rememberMe: false, the session cookie is created and stored correctly (verified in browser + DB), but /v1/session (or /api/auth/get-session) returns null. With rememberMe: true, everything works fine. Both od the endpoints returns the session. What i've tried: Cookies are correctly set (better-auth.session_token, dont_remember) and sent with credentials: "include". Headers forwarded correctly in /v1/login, including Set-Cookie. Tried removing secure, no change. Session exists in the DB. When visiting /v1/session, still getting null. I'm using Next.js as the frontend and the Hono as separate backend where better auth is running. Here's the login route:
import { Hono } from "hono";
import { auth } from "../../lib/auth";

const loginRoute = new Hono();

loginRoute.post("/", async (c) => {
const { email, password, rememberMe } = await c.req.json();

const { headers, response } = await auth.api.signInEmail({
body: { email, password, rememberMe },
returnHeaders: true,
});


// parse and set cookies
headers.forEach((value, name) => {
if (name.toLowerCase() === "set-cookie") {
c.header("Set-Cookie", value);
}
});

return c.json(response);
});

export default loginRoute;
import { Hono } from "hono";
import { auth } from "../../lib/auth";

const loginRoute = new Hono();

loginRoute.post("/", async (c) => {
const { email, password, rememberMe } = await c.req.json();

const { headers, response } = await auth.api.signInEmail({
body: { email, password, rememberMe },
returnHeaders: true,
});


// parse and set cookies
headers.forEach((value, name) => {
if (name.toLowerCase() === "set-cookie") {
c.header("Set-Cookie", value);
}
});

return c.json(response);
});

export default loginRoute;
2 replies
BABetter Auth
Created by sebastian on 5/7/2025 in #help
getting unauthorized in production but locally works flawless
Hello. Im trying to implement a user check in one of my api endpoints in Hono to prevent abuse. The whole auth flow was setup on nextjs side (logging in, signing up etc.). But i neeed separate backend to manage high uploads. And the problem rises when trying to fetch that protected endpoint. I fetch it client-side with credentials "include" to attach the cookies. After fetching, i always get 401, and the credentials are set to "omit" for some reason. This didn't happen in local. Here's the fetch call:
const res = await fetch(`${API_URL}/v1/auth/upload`, {
method: "POST",
credentials: "include",
body: formData,
});
const res = await fetch(`${API_URL}/v1/auth/upload`, {
method: "POST",
credentials: "include",
body: formData,
});
Here's the server configuration:
import { Hono } from 'hono'
import { auth } from './lib/auth'
import { cors } from 'hono/cors'
import { config } from 'dotenv'
import routes from './routes'
config({ path: '.env' })

const app = new Hono()

app.use(
'*',
cors({
origin: [process.env.CLIENT_URL!, process.env.BETTER_AUTH_URL!], // Specify allowed origins
credentials: true, // Allow credentials (cookies, etc.)
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
exposeHeaders: ["Content-Length"],
allowHeaders: ["Content-Type", "Authorization", "Cookie"],
maxAge: 600, // Cache preflight for 10 minutes
})
)

app.all('/api/auth/*', async (c) => {
return await auth.handler(c.req.raw)
})

app.get('/', (c) => {
return c.json({
message: 'Root route'
})
})

app.route("/v1", routes)


export default {
port: 8080,
fetch: app.fetch,
}
import { Hono } from 'hono'
import { auth } from './lib/auth'
import { cors } from 'hono/cors'
import { config } from 'dotenv'
import routes from './routes'
config({ path: '.env' })

const app = new Hono()

app.use(
'*',
cors({
origin: [process.env.CLIENT_URL!, process.env.BETTER_AUTH_URL!], // Specify allowed origins
credentials: true, // Allow credentials (cookies, etc.)
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
exposeHeaders: ["Content-Length"],
allowHeaders: ["Content-Type", "Authorization", "Cookie"],
maxAge: 600, // Cache preflight for 10 minutes
})
)

app.all('/api/auth/*', async (c) => {
return await auth.handler(c.req.raw)
})

app.get('/', (c) => {
return c.json({
message: 'Root route'
})
})

app.route("/v1", routes)


export default {
port: 8080,
fetch: app.fetch,
}
And heres the endpoint: /v1/auth/upload:
import { Hono } from "hono";
import { auth } from "../lib/auth";
import { AuthSession } from "../lib/auth-types";
import { createClient } from "@supabase/supabase-js";

const uploadRoute = new Hono<AuthSession>();

const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
);

uploadRoute.post("/upload", async (c) => {
const session = await auth.api.getSession(c.req.raw);
const user = session?.user;

if (!user) {
return c.json(null, 401);
}

const formData = await c.req.formData();
const files = formData.getAll("files");

if (!files || files.length === 0) {
return c.json(
{
message: "No files uploaded",
},
400
);
}

// later REPLACE with a title, user id, etc*.
const randomFolderName = Math.random().toString(36).substring(2, 15);

try {
await Promise.all(
files.map(async (file) => {
const { error } = await supabase.storage
.from("upload_files_bucket")
.upload(
`${randomFolderName}/${file instanceof File ? file.name : "unknown"}`,
file as File,
{
cacheControl: "3600",
upsert: false,
}
);
if (error) {
throw new Error("An supabase error occurred while uploading the file");
}
})
);

return c.json({
message: "Files uploaded",
}, 200);
} catch (error) {
return c.json({
message: "Error uploading files",
}, 500);
}
});


export default uploadRoute;
import { Hono } from "hono";
import { auth } from "../lib/auth";
import { AuthSession } from "../lib/auth-types";
import { createClient } from "@supabase/supabase-js";

const uploadRoute = new Hono<AuthSession>();

const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
);

uploadRoute.post("/upload", async (c) => {
const session = await auth.api.getSession(c.req.raw);
const user = session?.user;

if (!user) {
return c.json(null, 401);
}

const formData = await c.req.formData();
const files = formData.getAll("files");

if (!files || files.length === 0) {
return c.json(
{
message: "No files uploaded",
},
400
);
}

// later REPLACE with a title, user id, etc*.
const randomFolderName = Math.random().toString(36).substring(2, 15);

try {
await Promise.all(
files.map(async (file) => {
const { error } = await supabase.storage
.from("upload_files_bucket")
.upload(
`${randomFolderName}/${file instanceof File ? file.name : "unknown"}`,
file as File,
{
cacheControl: "3600",
upsert: false,
}
);
if (error) {
throw new Error("An supabase error occurred while uploading the file");
}
})
);

return c.json({
message: "Files uploaded",
}, 200);
} catch (error) {
return c.json({
message: "Error uploading files",
}, 500);
}
});


export default uploadRoute;
9 replies