Files
crumbs/src/lib/utils/auth-methods.test.ts
T
Frederic Cabassut e4606cfc70 feat(auth): enable password login on OAuth-only accounts, with disable control
Admins can now reset the password of an OAuth-only user, which creates
their credentials and enables password login; the new 'Disable password
login' row action removes those credentials again. Resetting a password
revokes all existing sessions.

- add users.password_login_enabled column (migration 0005)
- admin reset: set password, enable login, revoke sessions
- disable action: clear password, disable login (only on OAuth-linked rows)
- account endpoint: refuse password updates on OAuth-linked users
- e2e: wipe the test DB before the webServer starts (Playwright starts
  the server before globalSetup, so wiping there orphaned the DB file)
- row-actions strip test: five square actions, wrap on phone widths
2026-08-19 13:18:56 +02:00

96 lines
3.1 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
canResetPassword,
passwordResetBlockedReason,
requiresPasswordForAccountDeletion,
canDisablePasswordLogin
} from './auth-methods.js';
describe('canResetPassword', () => {
it('allows a reset for a password account', () => {
expect(canResetPassword({ authProvider: 'password', passwordLoginEnabled: true })).toBe(true);
});
it('allows a reset for an OAuth-only account', () => {
expect(canResetPassword({ authProvider: 'none', passwordLoginEnabled: false })).toBe(true);
});
it('allows a reset for an OAuth-linked account', () => {
expect(canResetPassword({ authProvider: 'google', passwordLoginEnabled: true })).toBe(true);
});
});
describe('passwordResetBlockedReason', () => {
it('returns null for every account type', () => {
for (const user of [
{ authProvider: 'password', passwordLoginEnabled: true },
{ authProvider: 'none', passwordLoginEnabled: false },
{ authProvider: 'google', passwordLoginEnabled: true },
{ authProvider: 'oidc', passwordLoginEnabled: false }
]) {
expect(passwordResetBlockedReason(user)).toBeNull();
}
});
});
describe('requiresPasswordForAccountDeletion', () => {
it('requires password for a pure password account', () => {
expect(
requiresPasswordForAccountDeletion({ authProvider: 'password', passwordLoginEnabled: true })
).toBe(true);
});
it('requires password for OAuth-only with admin-enabled password login', () => {
expect(
requiresPasswordForAccountDeletion({ authProvider: 'none', passwordLoginEnabled: true })
).toBe(true);
});
it('does not require password for OAuth-linked accounts even when password login is enabled', () => {
for (const authProvider of ['google', 'github', 'oidc']) {
expect(
requiresPasswordForAccountDeletion({ authProvider, passwordLoginEnabled: true }),
authProvider
).toBe(false);
}
});
it('does not require password for OAuth-only without password login', () => {
expect(
requiresPasswordForAccountDeletion({ authProvider: 'none', passwordLoginEnabled: false })
).toBe(false);
});
});
describe('canDisablePasswordLogin', () => {
it('allows disabling for an OAuth-linked user with password login enabled', () => {
expect(
canDisablePasswordLogin({ id: 2, passwordLoginEnabled: true, authProvider: 'google' }, 1)
).toBe(true);
});
it('blocks disabling on the admin own row', () => {
expect(
canDisablePasswordLogin({ id: 1, passwordLoginEnabled: true, authProvider: 'google' }, 1)
).toBe(false);
});
it('blocks disabling when password login is already off', () => {
expect(
canDisablePasswordLogin({ id: 2, passwordLoginEnabled: false, authProvider: 'google' }, 1)
).toBe(false);
});
it('blocks disabling on a password-only account', () => {
expect(
canDisablePasswordLogin({ id: 2, passwordLoginEnabled: true, authProvider: 'password' }, 1)
).toBe(false);
});
it('blocks disabling on OAuth-only with admin-enabled password but no provider link', () => {
expect(
canDisablePasswordLogin({ id: 2, passwordLoginEnabled: true, authProvider: 'none' }, 1)
).toBe(false);
});
});