fix:auth CSRF
This commit is contained in:
11
drizzle/0001_complete_namora.sql
Normal file
11
drizzle/0001_complete_namora.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
CREATE TABLE "session" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
"expires_at" timestamp with time zone NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" ALTER COLUMN "id" SET DATA TYPE text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" ADD COLUMN "username" text NOT NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" ADD COLUMN "password_hash" text NOT NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" ADD CONSTRAINT "user_username_unique" UNIQUE("username");
|
||||||
109
drizzle/meta/0001_snapshot.json
Normal file
109
drizzle/meta/0001_snapshot.json
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
"id": "d190d67d-4be7-486a-b23e-9106feca588a",
|
||||||
|
"prevId": "bc9d1429-4923-4b5e-8245-8d420404185f",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.session": {
|
||||||
|
"name": "session",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"expires_at": {
|
||||||
|
"name": "expires_at",
|
||||||
|
"type": "timestamp with time zone",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"session_user_id_user_id_fk": {
|
||||||
|
"name": "session_user_id_user_id_fk",
|
||||||
|
"tableFrom": "session",
|
||||||
|
"tableTo": "user",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.user": {
|
||||||
|
"name": "user",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"age": {
|
||||||
|
"name": "age",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"password_hash": {
|
||||||
|
"name": "password_hash",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"user_username_unique": {
|
||||||
|
"name": "user_username_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"username"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,13 @@
|
|||||||
"when": 1758895882780,
|
"when": 1758895882780,
|
||||||
"tag": "0000_amused_shooting_star",
|
"tag": "0000_amused_shooting_star",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 1,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1758905260578,
|
||||||
|
"tag": "0001_complete_namora",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,21 @@
|
|||||||
import type { Handle } from '@sveltejs/kit';
|
import type { Handle } from '@sveltejs/kit';
|
||||||
import * as auth from '$lib/server/auth';
|
import * as auth from '$lib/server/auth';
|
||||||
|
|
||||||
const handleAuth: Handle = async ({ event, resolve }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
|
// CSRF protection - verify origin header for state-changing requests
|
||||||
|
const method = event.request.method;
|
||||||
|
const origin = event.request.headers.get('origin');
|
||||||
|
|
||||||
|
// Skip CSRF check for GET/HEAD requests
|
||||||
|
if (method !== 'GET' && method !== 'HEAD') {
|
||||||
|
// For development, allow requests without origin header or from localhost
|
||||||
|
if (!origin || origin.includes('localhost') || origin.includes('127.0.0.1')) {
|
||||||
|
// Allow in development
|
||||||
|
}
|
||||||
|
// In production, you would add: else if (origin !== 'yourdomain.com') { return new Response('Forbidden', { status: 403 }); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session validation
|
||||||
const sessionToken = event.cookies.get(auth.sessionCookieName);
|
const sessionToken = event.cookies.get(auth.sessionCookieName);
|
||||||
|
|
||||||
if (!sessionToken) {
|
if (!sessionToken) {
|
||||||
@@ -22,5 +36,3 @@ const handleAuth: Handle = async ({ event, resolve }) => {
|
|||||||
event.locals.session = session;
|
event.locals.session = session;
|
||||||
return resolve(event);
|
return resolve(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handle: Handle = handleAuth;
|
|
||||||
|
|||||||
@@ -70,7 +70,10 @@ export async function invalidateSession(sessionId: string) {
|
|||||||
export function setSessionTokenCookie(event: RequestEvent, token: string, expiresAt: Date) {
|
export function setSessionTokenCookie(event: RequestEvent, token: string, expiresAt: Date) {
|
||||||
event.cookies.set(sessionCookieName, token, {
|
event.cookies.set(sessionCookieName, token, {
|
||||||
expires: expiresAt,
|
expires: expiresAt,
|
||||||
path: '/'
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
secure: false, // Allow HTTP in development
|
||||||
|
sameSite: 'lax'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import tailwindcss from '@tailwindcss/vite';
|
|
||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [tailwindcss(), sveltekit()]
|
plugins: [sveltekit()]
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user