Files
trips/src/lib/server/local-credentials.ts
AI Agent d44db1489c
Some checks failed
PR Checks / lint-test-and-docker-build (pull_request) Failing after 23s
trips) add local auth provider and login picker
2026-02-22 04:52:17 +00:00

68 lines
2.1 KiB
TypeScript

import { env } from '$env/dynamic/private';
import argon2 from 'argon2';
import { db } from './db/index.js';
export interface LocalCredentialRecord {
userId: string;
username: string;
fullName: string;
email: string | null;
passwordHash: string;
}
export const LOCAL_AUTH_MIN_PASSWORD_LENGTH = 12;
const getNumberEnv = (value: string | undefined, fallback: number): number => {
const parsed = Number.parseInt(value ?? '', 10);
return Number.isFinite(parsed) ? parsed : fallback;
};
export function getLocalCredentialByIdentifier(identifier: string): LocalCredentialRecord | null {
const normalized = identifier.trim();
if (!normalized) return null;
return (
db.get<LocalCredentialRecord>(
`SELECT
users.id as userId,
users.username as username,
users.full_name as fullName,
users.email as email,
local_credentials.password_hash as passwordHash
FROM users
INNER JOIN local_credentials ON local_credentials.user_id = users.id
WHERE lower(users.username) = lower(?)
OR (users.email IS NOT NULL AND lower(users.email) = lower(?))
LIMIT 1`,
[normalized, normalized]
) ?? null
);
}
export async function hashLocalPassword(password: string): Promise<string> {
const trimmed = password.trim();
if (trimmed.length < LOCAL_AUTH_MIN_PASSWORD_LENGTH) {
throw new Error(`Password must be at least ${LOCAL_AUTH_MIN_PASSWORD_LENGTH} characters`);
}
const memoryCost = getNumberEnv(env.LOCAL_AUTH_ARGON2_MEMORY_KB, 65536);
const timeCost = getNumberEnv(env.LOCAL_AUTH_ARGON2_TIME_COST, 3);
const parallelism = getNumberEnv(env.LOCAL_AUTH_ARGON2_PARALLELISM, 1);
return argon2.hash(trimmed, {
type: argon2.argon2id,
memoryCost,
timeCost,
parallelism
});
}
export async function setLocalCredentialPassword(userId: string, password: string): Promise<void> {
const passwordHash = await hashLocalPassword(password);
db.run(
`INSERT INTO local_credentials (user_id, password_hash)
VALUES (?, ?)
ON CONFLICT(user_id) DO UPDATE SET
password_hash = excluded.password_hash,
updated_at = datetime('now')`,
[userId, passwordHash]
);
}