Lucia Authentication #8

Merged
DanMihailescu merged 18 commits from undoPretty into master 2024-12-19 20:06:45 -05:00
10 changed files with 181 additions and 12 deletions
Showing only changes of commit a6c6b4a7a7 - Show all commits

Binary file not shown.

View file

@ -0,0 +1,41 @@
/*
Warnings:
- Added the required column `password` to the `User` table without a default value. This is not possible if the table is not empty.
*/
-- CreateTable
CREATE TABLE "Session" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"expiresAt" DATETIME NOT NULL,
"sessionToken" TEXT NOT NULL,
"accessToken" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"userId" INTEGER NOT NULL,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"email" TEXT,
"name" TEXT NOT NULL,
"password" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO "new_User" ("createdAt", "email", "id", "name", "updatedAt") SELECT "createdAt", "email", "id", "name", "updatedAt" FROM "User";
DROP TABLE "User";
ALTER TABLE "new_User" RENAME TO "User";
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
-- CreateIndex
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
-- CreateIndex
CREATE UNIQUE INDEX "Session_accessToken_key" ON "Session"("accessToken");

View file

@ -0,0 +1,23 @@
/*
Warnings:
- You are about to drop the column `accessToken` on the `Session` table. All the data in the column will be lost.
- You are about to drop the column `sessionToken` on the `Session` table. All the data in the column will be lost.
*/
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Session" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"expiresAt" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"userId" INTEGER NOT NULL,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Session" ("createdAt", "expiresAt", "id", "updatedAt", "userId") SELECT "createdAt", "expiresAt", "id", "updatedAt", "userId" FROM "Session";
DROP TABLE "Session";
ALTER TABLE "new_Session" RENAME TO "Session";
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View file

@ -0,0 +1,22 @@
/*
Warnings:
- The primary key for the `Session` table will be changed. If it partially fails, the table could be left without primary key constraint.
*/
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Session" (
"id" TEXT NOT NULL PRIMARY KEY,
"expiresAt" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"userId" INTEGER NOT NULL,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Session" ("createdAt", "expiresAt", "id", "updatedAt", "userId") SELECT "createdAt", "expiresAt", "id", "updatedAt", "userId" FROM "Session";
DROP TABLE "Session";
ALTER TABLE "new_Session" RENAME TO "Session";
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View file

@ -0,0 +1,47 @@
/*
Warnings:
- The primary key for the `User` table will be changed. If it partially fails, the table could be left without primary key constraint.
*/
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Post" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"published" BOOLEAN DEFAULT false,
"authorId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Post" ("authorId", "content", "createdAt", "id", "published", "title", "updatedAt") SELECT "authorId", "content", "createdAt", "id", "published", "title", "updatedAt" FROM "Post";
DROP TABLE "Post";
ALTER TABLE "new_Post" RENAME TO "Post";
CREATE TABLE "new_Session" (
"id" TEXT NOT NULL PRIMARY KEY,
"expiresAt" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"userId" TEXT NOT NULL,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Session" ("createdAt", "expiresAt", "id", "updatedAt", "userId") SELECT "createdAt", "expiresAt", "id", "updatedAt", "userId" FROM "Session";
DROP TABLE "Session";
ALTER TABLE "new_Session" RENAME TO "Session";
CREATE TABLE "new_User" (
"id" TEXT NOT NULL PRIMARY KEY,
"email" TEXT,
"name" TEXT NOT NULL,
"password" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO "new_User" ("createdAt", "email", "id", "name", "password", "updatedAt") SELECT "createdAt", "email", "id", "name", "password", "updatedAt" FROM "User";
DROP TABLE "User";
ALTER TABLE "new_User" RENAME TO "User";
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View file

@ -0,0 +1,24 @@
/*
Warnings:
- The primary key for the `Post` table will be changed. If it partially fails, the table could be left without primary key constraint.
*/
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Post" (
"id" TEXT NOT NULL PRIMARY KEY,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"published" BOOLEAN DEFAULT false,
"authorId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Post" ("authorId", "content", "createdAt", "id", "published", "title", "updatedAt") SELECT "authorId", "content", "createdAt", "id", "published", "title", "updatedAt" FROM "Post";
DROP TABLE "Post";
ALTER TABLE "new_Post" RENAME TO "Post";
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View file

@ -15,9 +15,10 @@ datasource db {
} }
model User { model User {
id Int @id @default(autoincrement()) id String @id @default(uuid())
email String? @unique email String? @unique
name String name String
password String
posts Post[] posts Post[]
sessions Session[] sessions Session[]
@ -26,24 +27,22 @@ model User {
} }
model Session { model Session {
id Int @id @default(autoincrement()) id String @id @default(uuid())
expiresAt DateTime expiresAt DateTime
sessionToken String @unique
accessToken String @unique
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
userId Int userId String
user User @relation(references: [id], fields: [userId]) user User @relation(references: [id], fields: [userId])
} }
BenjaminPalko commented 2024-12-17 14:10:51 -05:00 (Migrated from github.com)
Review

NIT: references and fields is backwards from how post shows it, can we keep them consistent

NIT: references and fields is backwards from how post shows it, can we keep them consistent
model Post { model Post {
id Int @id @default(autoincrement()) id String @id @default(uuid())
title String title String
content String content String
published Boolean? @default(false) published Boolean? @default(false)
author User @relation(fields: [authorId], references: [id]) author User @relation(fields: [authorId], references: [id])
authorId Int authorId String
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
} }

6
src/app.d.ts vendored
View file

@ -2,8 +2,12 @@
// for information about these interfaces // for information about these interfaces
declare global { declare global {
namespace App { namespace App {
// interface Error {} // interface Error {}
// interface Locals {} interface Locals {
user: import("lucia").User | null;
session: import('lucia').Session | null;
}
// interface PageData {} // interface PageData {}
// interface PageState {} // interface PageState {}
// interface Platform {} // interface Platform {}

View file

@ -8,7 +8,7 @@ const adapter = new PrismaAdapter(client.session, client.user)
BenjaminPalko commented 2024-12-17 14:11:26 -05:00 (Migrated from github.com)
Review

Reuse the $lib/server/prisma client plz

Reuse the `$lib/server/prisma` client plz
BenjaminPalko commented 2024-12-18 12:33:08 -05:00 (Migrated from github.com)
Review

You dont need to set it to a const...

You dont need to set it to a const...
BenjaminPalko commented 2024-12-17 14:11:26 -05:00 (Migrated from github.com)
Review

Reuse the $lib/server/prisma client plz

Reuse the `$lib/server/prisma` client plz
BenjaminPalko commented 2024-12-18 12:33:08 -05:00 (Migrated from github.com)
Review

You dont need to set it to a const...

You dont need to set it to a const...
export const auth = new Lucia(adapter, { export const auth = new Lucia(adapter, {
sessionCookie: { sessionCookie: {
attributes: { attributes: {
// secure: process.env.NODE_ENV === "production" secure: process.env.NODE_ENV === "production"
BenjaminPalko commented 2024-12-17 14:11:26 -05:00 (Migrated from github.com)
Review

Reuse the $lib/server/prisma client plz

Reuse the `$lib/server/prisma` client plz
BenjaminPalko commented 2024-12-18 12:33:08 -05:00 (Migrated from github.com)
Review

You dont need to set it to a const...

You dont need to set it to a const...
BenjaminPalko commented 2024-12-17 14:11:26 -05:00 (Migrated from github.com)
Review

Reuse the $lib/server/prisma client plz

Reuse the `$lib/server/prisma` client plz
BenjaminPalko commented 2024-12-18 12:33:08 -05:00 (Migrated from github.com)
Review

You dont need to set it to a const...

You dont need to set it to a const...
} }
}, },
getUserAttributes: (attributes)=>{ getUserAttributes: (attributes)=>{

BenjaminPalko commented 2024-12-17 14:11:26 -05:00 (Migrated from github.com)
Review

Reuse the $lib/server/prisma client plz

Reuse the `$lib/server/prisma` client plz
BenjaminPalko commented 2024-12-18 12:33:08 -05:00 (Migrated from github.com)
Review

You dont need to set it to a const...

You dont need to set it to a const...
BenjaminPalko commented 2024-12-17 14:11:26 -05:00 (Migrated from github.com)
Review

Reuse the $lib/server/prisma client plz

Reuse the `$lib/server/prisma` client plz
BenjaminPalko commented 2024-12-18 12:33:08 -05:00 (Migrated from github.com)
Review

You dont need to set it to a const...

You dont need to set it to a const...

View file

@ -1,6 +1,10 @@
import { logger } from '$lib/server/logger'; import { logger } from '$lib/server/logger';
import { prisma } from '$lib/server/prisma'; import { prisma } from '$lib/server/prisma';
import { error, redirect, type Actions } from '@sveltejs/kit'; import { error, redirect, type Actions } from '@sveltejs/kit';
//import { password } from 'bun';
import { Argon2id } from "oslo/password"
import { generateId } from 'lucia';
import { auth } from '$lib/server/lucia.js';
export const actions = { export const actions = {
login: async (event) => { login: async (event) => {
@ -25,22 +29,27 @@ export const actions = {
}, },
register: async (event) => { register: async (event) => {
const form = await event.request.formData(); const form = await event.request.formData();
if (!form.has('email') || !form.has('name')) { if (!form.has('email') || !form.has('name') || !form.has('password')) {
return error(400); return error(400);
} }
const hashedPassword = await new Argon2id().hash(form.get('password') as string)
const user = await prisma.user.create({ const user = await prisma.user.create({
data: { data: {
email: form.get('email') as string, email: form.get('email') as string,
name: form.get('name') as string name: form.get('name') as string,
password: hashedPassword
} }
}); });
const session = await auth.createSession(user.id.toString(), {});
const sessionCookie = auth.createSessionCookie(session.id);
if (!user) { if (!user) {
return error(500); return error(500);
} }
event.cookies.set('user', String(user.id), { event.cookies.set(sessionCookie.name, sessionCookie.value, {
path: '/', path: '/',
maxAge: 120 maxAge: 120
}); });
redirect(302, '/'); redirect(302, '/');
} }
} satisfies Actions; } satisfies Actions;