Autogen SolidJS template with auth

This commit is contained in:
2026-02-04 21:37:21 +02:00
parent b2d2409357
commit 5b544f5818
27 changed files with 2063 additions and 0 deletions

85
ui/src/auth/server.ts Normal file
View File

@@ -0,0 +1,85 @@
import { redirect } from "@solidjs/router";
import { useSession } from "vinxi/http";
import { getRandomValues, subtle, timingSafeEqual } from "crypto";
import { createUser, findUser } from "./db";
export interface Session {
id: number;
email: string;
}
export const getSession = () =>
useSession<Session>({
password: process.env.SESSION_SECRET!,
});
export async function createSession(user: Session, redirectTo?: string) {
const validDest = redirectTo?.[0] === "/" && redirectTo[1] !== "/";
const session = await getSession();
await session.update(user);
return redirect(validDest ? redirectTo : "/");
}
async function createHash(password: string) {
const salt = getRandomValues(new Uint8Array(16));
const saltHex = Buffer.from(salt).toString("hex");
const key = await subtle.deriveBits(
{
name: "PBKDF2",
salt,
iterations: 100_000,
hash: "SHA-512",
},
await subtle.importKey(
"raw",
new TextEncoder().encode(password),
"PBKDF2",
false,
["deriveBits"],
),
512,
);
const hash = Buffer.from(key).toString("hex");
return `${saltHex}:${hash}`;
}
async function checkPassword(storedPassword: string, providedPassword: string) {
const [storedSalt, storedHash] = storedPassword.split(":");
if (!storedSalt || !storedHash)
throw new Error("Invalid stored password format");
const key = await subtle.deriveBits(
{
name: "PBKDF2",
salt: Buffer.from(storedSalt, "hex"),
iterations: 100_000,
hash: "SHA-512",
},
await subtle.importKey(
"raw",
new TextEncoder().encode(providedPassword),
"PBKDF2",
false,
["deriveBits"],
),
512,
);
const hash = Buffer.from(key);
const stored = Buffer.from(storedHash, "hex");
if (stored.length !== hash.length || !timingSafeEqual(stored, hash))
throw new Error("Invalid email or password");
}
export async function passwordLogin(email: string, password: string) {
let user = await findUser({ email });
if (!user)
user = await createUser({
email,
password: await createHash(password),
});
else if (!user.password)
throw new Error(
"Account exists via OAuth. Sign in with your OAuth provider",
);
else await checkPassword(user.password, password);
return createSession(user);
}