fix(auth): correct local cookie security configuration

This commit is contained in:
Schubert Ferenc 2026-07-11 15:50:15 +02:00
parent b584e60273
commit 155fdbb16a
67 changed files with 1003 additions and 62 deletions

View file

@ -1,4 +1,4 @@
export const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL ?? "/api/v1";
export const API_BASE = "/api/v1";
export type Entity = {
id: string;
@ -121,21 +121,25 @@ export type Paginated<T> = {
};
export async function login(email: string, password: string) {
const response = await fetch(`${API_BASE}/auth/login`, {
const response = await fetch(`/api/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
body: JSON.stringify({ email, password }),
credentials: "include"
});
if (!response.ok) {
throw new Error("Anmeldung fehlgeschlagen");
const detail = await response.text();
const error = new Error(detail || `HTTP ${response.status}`) as Error & { status?: number };
error.status = response.status;
throw error;
}
return response.json() as Promise<{ access_token: string; token_type: string }>;
return response.json() as Promise<{ user: User; expires_in: number }>;
}
export async function apiGet<T>(path: string, token: string): Promise<T> {
const response = await fetch(`${API_BASE}${path}`, {
headers: { Authorization: `Bearer ${token}` },
cache: "no-store"
cache: "no-store",
credentials: "include"
});
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`);
@ -146,8 +150,9 @@ export async function apiGet<T>(path: string, token: string): Promise<T> {
export async function apiSend<T>(path: string, token: string, method: "POST" | "PUT", body: unknown): Promise<T> {
const response = await fetch(`${API_BASE}${path}`, {
method,
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify(body)
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
credentials: "include"
});
if (!response.ok) {
const detail = await response.text();
@ -159,7 +164,7 @@ export async function apiSend<T>(path: string, token: string, method: "POST" | "
export async function apiDelete(path: string, token: string): Promise<void> {
const response = await fetch(`${API_BASE}${path}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${token}` }
credentials: "include"
});
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`);
@ -169,7 +174,8 @@ export async function apiDelete(path: string, token: string): Promise<void> {
export async function apiFetch<T>(path: string, token: string, init?: RequestInit): Promise<T> {
const response = await fetch(`${API_BASE}${path}`, {
...init,
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...(init?.headers ?? {}) }
headers: { "Content-Type": "application/json", ...(init?.headers ?? {}) },
credentials: "include"
});
if (!response.ok) {
throw new Error(await response.text());

View file

@ -0,0 +1,13 @@
export const AUTH_COOKIE_NAME = process.env.AUTH_COOKIE_NAME ?? "atlas_access_token";
export const AUTH_COOKIE_SECURE = process.env.AUTH_COOKIE_SECURE?.trim().toLowerCase() === "true";
export const AUTH_COOKIE_SAMESITE = (process.env.AUTH_COOKIE_SAMESITE ?? "lax").trim().toLowerCase();
export function buildAuthCookieOptions(maxAge: number) {
return {
httpOnly: true as const,
secure: AUTH_COOKIE_SECURE,
sameSite: AUTH_COOKIE_SAMESITE as "lax" | "strict" | "none",
path: "/" as const,
maxAge
};
}