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;
Solution:
Got it working now after 7 hours. I needed to hard reset browser and remove all cookies for this to work. Something weird probably cached. The code was good.
Jump to solution
6 Replies
sebastian
sebastianOP3w ago
Also, earlier in the app i implement an server-side check to get the session from the backend and it worked flawlessy. I just can't get the credentials to work. (clientside)
import { cookies } from 'next/headers'

const getServerSession = async () => {
try {
const cookieHeader = (await cookies()).toString()

const res = await fetch(`${process.env.API_URL}/v1/auth/session`, {
headers: {
'Cookie': cookieHeader
}
})

return res.json()

} catch (error) {
console.error(error)
return null
}
}

export default getServerSession
import { cookies } from 'next/headers'

const getServerSession = async () => {
try {
const cookieHeader = (await cookies()).toString()

const res = await fetch(`${process.env.API_URL}/v1/auth/session`, {
headers: {
'Cookie': cookieHeader
}
})

return res.json()

} catch (error) {
console.error(error)
return null
}
}

export default getServerSession
kagann
kagann3w ago
check cookie domain
sebastian
sebastianOP3w ago
You mean the domain in the auth config? advanced: { crossSubDomainCookies: { enabled: true, domain: ".domain.xyz", } }, This is what im using both client and server
kagann
kagann3w ago
nope devtools
sebastian
sebastianOP3w ago
Can't really tell you, because no cookies are sent to the server beacuse of the credentials "omit" instead of includes. Every other domain in the request is correct.
Solution
sebastian
sebastian3w ago
Got it working now after 7 hours. I needed to hard reset browser and remove all cookies for this to work. Something weird probably cached. The code was good.

Did you find this page helpful?