From 23ff44dfaf16b94b7857d737f39d9dad0f59942f Mon Sep 17 00:00:00 2001 From: Frederic Cabassut Date: Mon, 9 Mar 2026 11:35:04 +0100 Subject: [PATCH] feat: allow creating OAuth-only users without a password Admin can now toggle "OAuth-only (no password)" when creating a user. These users have no password hash and authProvider set to 'none', so they can only log in via an OAuth/SSO provider. Co-Authored-By: Claude Opus 4.6 --- src/lib/server/auth.ts | 6 ++-- src/routes/(app)/settings/users/+page.svelte | 35 ++++++++++++++------ src/routes/api/admin/users/+server.ts | 8 ++--- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 3c81eec..ff48340 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -194,10 +194,10 @@ export function revokeAllSessions(userId: number, exceptToken?: string): void { export async function createUser( email: string, displayName: string, - password: string, + password: string | null, role: 'admin' | 'user' = 'user' ): Promise { - const hash = await argon2.hash(password); + const hash = password ? await argon2.hash(password) : null; const result = db .insert(users) .values({ @@ -205,7 +205,7 @@ export async function createUser( displayName, role, passwordHash: hash, - authProvider: 'password', + authProvider: password ? 'password' : 'none', createdAt: new Date() }) .returning() diff --git a/src/routes/(app)/settings/users/+page.svelte b/src/routes/(app)/settings/users/+page.svelte index 6f3cd32..14f9448 100644 --- a/src/routes/(app)/settings/users/+page.svelte +++ b/src/routes/(app)/settings/users/+page.svelte @@ -11,17 +11,23 @@ let newDisplayName = $state(''); let newPassword = $state(''); let newRole = $state<'admin' | 'user'>('user'); + let oauthOnly = $state(false); let createMsg = $state(''); let createError = $state(false); async function createUser() { createMsg = ''; - if (!newEmail || !newPassword) { - createMsg = 'Email and password are required'; + if (!newEmail) { + createMsg = 'Email is required'; createError = true; return; } - if (newPassword.length < 8) { + if (!oauthOnly && !newPassword) { + createMsg = 'Password is required (or enable OAuth-only)'; + createError = true; + return; + } + if (!oauthOnly && newPassword.length < 8) { createMsg = 'Password must be at least 8 characters'; createError = true; return; @@ -33,7 +39,7 @@ body: JSON.stringify({ email: newEmail, displayName: newDisplayName || newEmail.split('@')[0], - password: newPassword, + password: oauthOnly ? undefined : newPassword, role: newRole }) }); @@ -44,6 +50,7 @@ newDisplayName = ''; newPassword = ''; newRole = 'user'; + oauthOnly = false; createMsg = 'User created'; createError = false; } else { @@ -125,13 +132,19 @@ placeholder="Display name (optional)" class="w-full rounded-sm border border-[var(--border-subtle)] bg-[var(--bg-base)] px-4 py-2 text-sm text-[var(--text)] outline-none placeholder:text-[var(--text-muted)] focus:border-[var(--primary)]" /> - - + + {#if !oauthOnly} + + + {/if}