trips) add local auth provider and login picker
Some checks failed
PR Checks / lint-test-and-docker-build (pull_request) Failing after 23s
Some checks failed
PR Checks / lint-test-and-docker-build (pull_request) Failing after 23s
This commit is contained in:
67
src/lib/server/local-credentials.ts
Normal file
67
src/lib/server/local-credentials.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
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]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user