added betterauth
This commit is contained in:
20
src/env.ts
Normal file
20
src/env.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { defineEnvVars } from "@sveltejs/kit/env";
|
||||
|
||||
const required = (name: string) => (value?: string) => {
|
||||
if (!value) throw new Error(`${name} is required`);
|
||||
return value;
|
||||
};
|
||||
|
||||
const optional = (value?: string) => value || undefined;
|
||||
|
||||
export const variables = defineEnvVars({
|
||||
DATABASE_URL: {
|
||||
description: 'Postgres connection string',
|
||||
schema: required('DATABASE_URL')
|
||||
},
|
||||
BETTER_AUTH_SECRET: { schema: required('BETTER_AUTH_SECRET') },
|
||||
BETTER_AUTH_URL: { schema: required('BETTER_AUTH_URL') },
|
||||
OIDC_ISSUER_DISCOVERY_URL: { schema: optional },
|
||||
OIDC_CLIENT_ID: { schema: optional },
|
||||
OIDC_CLIENT_SECRET: { schema: optional },
|
||||
});
|
||||
36
src/lib/server/auth.ts
Normal file
36
src/lib/server/auth.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { genericOAuth } from "better-auth/plugins";
|
||||
import {
|
||||
BETTER_AUTH_SECRET,
|
||||
BETTER_AUTH_URL,
|
||||
OIDC_CLIENT_ID,
|
||||
OIDC_CLIENT_SECRET,
|
||||
OIDC_ISSUER_DISCOVERY_URL
|
||||
} from '$app/env/private';
|
||||
import { betterAuth } from "better-auth";
|
||||
import { prismaAdapter } from "better-auth/adapters/prisma";
|
||||
import { db } from "./db";
|
||||
import { sveltekitCookies } from "better-auth/svelte-kit";
|
||||
import { getRequestEvent } from "$app/server";
|
||||
|
||||
const oidc = OIDC_ISSUER_DISCOVERY_URL && OIDC_CLIENT_ID && OIDC_CLIENT_SECRET
|
||||
? [
|
||||
genericOAuth({
|
||||
config: [
|
||||
{
|
||||
providerId: 'oidc',
|
||||
clientId: OIDC_CLIENT_ID,
|
||||
clientSecret: OIDC_CLIENT_SECRET,
|
||||
discoveryUrl: OIDC_ISSUER_DISCOVERY_URL,
|
||||
scopes: ['openid', 'profile', 'email'],
|
||||
}
|
||||
]
|
||||
})
|
||||
] : [];
|
||||
|
||||
export const auth = betterAuth({
|
||||
secret: BETTER_AUTH_SECRET,
|
||||
baseURL: BETTER_AUTH_URL,
|
||||
database: prismaAdapter(db, { provider: 'postgresql' }),
|
||||
emailAndPassword: { enabled: true },
|
||||
plugins: [...oidc, sveltekitCookies(getRequestEvent)],
|
||||
});
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
import { PrismaClient } from '#lib/generated/prisma/client.ts';
|
||||
import { dev } from '$app/env';
|
||||
import { DATABASE_URL } from '$app/env/private';
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import { env } from 'process';
|
||||
|
||||
const globalForDb = globalThis as unknown as { db?: PrismaClient };
|
||||
|
||||
function createClient() {
|
||||
const adapter = new PrismaPg({ connectionString: env.DATABASE_URL });
|
||||
const adapter = new PrismaPg({ connectionString: DATABASE_URL });
|
||||
return new PrismaClient({ adapter });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
import { db } from '../../../lib/server/db';
|
||||
import { error, fail, redirect } from '@sveltejs/kit';
|
||||
import { APIError } from 'better-auth/api';
|
||||
import { db } from '#lib/server/db.ts';
|
||||
import { auth } from '#lib/server/auth.ts';
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
if ((await db.user.count()) > 0) redirect(303, '/login');
|
||||
if ((await db.membership.count()) > 0) redirect(303, '/login');
|
||||
}
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({ request }) => {
|
||||
if ((await db.user.count()) > 0) error(403, 'Setup has already been completed');
|
||||
if ((await db.membership.count()) > 0) error(403, 'Setup has already been completed');
|
||||
|
||||
const data = await request.formData();
|
||||
const email = String(data.get('email') ?? '').trim().toLowerCase();
|
||||
const name = String(data.get('name') ?? '').trim();
|
||||
const username = String(data.get('username') ?? '').trim();
|
||||
const password = String(data.get('password') ?? '');
|
||||
const mode = String(data.get('mode') ?? '');
|
||||
@@ -25,23 +28,35 @@ export const actions: Actions = {
|
||||
if (Object.keys(errors).length > 0) {
|
||||
return fail(400, { errors, values: { email, username }});
|
||||
}
|
||||
// need a hashing mechanism
|
||||
// const passwordHash = await hashPassword(password)
|
||||
|
||||
await db.$transaction(async (tx) => {
|
||||
const user = await tx.user.create({
|
||||
data: { email, username, password, authProvider: 'credentials', isAdmin: true }
|
||||
});
|
||||
const project = await tx.project.create({ data: { name: 'My Project' } });
|
||||
await tx.membership.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
projectId: project.id,
|
||||
role: mode === 'planner' ? 'PLANNER' : 'COUPLE'
|
||||
}
|
||||
let userId: string;
|
||||
try {
|
||||
const result = await auth.api.signUpEmail({ body: { name, email, password } });
|
||||
userId = result.user.id;
|
||||
} catch (e) {
|
||||
if (e instanceof APIError) {
|
||||
errors.form = e.message;
|
||||
return fail(400, { errors, values: { email, name } });
|
||||
}
|
||||
throw 0
|
||||
}
|
||||
|
||||
try {
|
||||
await db.$transaction(async (tx) => {
|
||||
const project = await tx.project.create({ data: { name: 'My Project' } });
|
||||
await tx.membership.create({
|
||||
data: {
|
||||
userId,
|
||||
projectId: project.id,
|
||||
role: mode === 'planner' ? 'PLANNER' : 'COUPLE'
|
||||
}
|
||||
});
|
||||
await tx.user.update({ where: { id: userId }, data: { isAdmin: true } });
|
||||
});
|
||||
});
|
||||
|
||||
redirect(303, '/login')
|
||||
} catch (e) {
|
||||
await db.user.delete({ where: { id: userId } });
|
||||
throw e;
|
||||
}
|
||||
redirect(303, '/')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user