auth-v2-workspace.test.ts86 lines · main
| 1 | import { describe, expect, test } from 'bun:test'; |
| 2 | |
| 3 | import { DEFAULT_AUTH_CONFIG, type AuthConfig } from './tenant-config-store.js'; |
| 4 | import { |
| 5 | flagsFromConfig, |
| 6 | hasAtLeastOneProvider, |
| 7 | normalizeTwoFactorFlags, |
| 8 | twoFactorFromConfig, |
| 9 | type AuthV2ProviderFlags, |
| 10 | } from './auth-v2-workspace.js'; |
| 11 | |
| 12 | describe('auth-v2-workspace — provider proof helpers', () => { |
| 13 | test('flagsFromConfig maps DEFAULT starter pack (all core ON)', () => { |
| 14 | const flags = flagsFromConfig(DEFAULT_AUTH_CONFIG); |
| 15 | expect(flags).toEqual({ |
| 16 | emailPassword: true, |
| 17 | magicLink: true, |
| 18 | emailOtp: true, |
| 19 | passkey: true, |
| 20 | }); |
| 21 | expect(hasAtLeastOneProvider(flags)).toBe(true); |
| 22 | }); |
| 23 | |
| 24 | test('flagsFromConfig reflects partial OFF (save-sticks shape)', () => { |
| 25 | const config = { |
| 26 | ...DEFAULT_AUTH_CONFIG, |
| 27 | providers: { |
| 28 | ...DEFAULT_AUTH_CONFIG.providers, |
| 29 | magicLink: { enabled: false, expiryMinutes: 15 }, |
| 30 | emailOtp: { enabled: false, codeLength: 6, expiryMinutes: 5 }, |
| 31 | passkey: { enabled: true }, |
| 32 | emailPassword: { enabled: true }, |
| 33 | }, |
| 34 | } as AuthConfig; |
| 35 | |
| 36 | const flags = flagsFromConfig(config); |
| 37 | expect(flags.magicLink).toBe(false); |
| 38 | expect(flags.emailOtp).toBe(false); |
| 39 | expect(flags.passkey).toBe(true); |
| 40 | expect(flags.emailPassword).toBe(true); |
| 41 | expect(hasAtLeastOneProvider(flags)).toBe(true); |
| 42 | }); |
| 43 | |
| 44 | test('hasAtLeastOneProvider rejects all-off (dashboard 400 rule)', () => { |
| 45 | const allOff: AuthV2ProviderFlags = { |
| 46 | emailPassword: false, |
| 47 | magicLink: false, |
| 48 | emailOtp: false, |
| 49 | passkey: false, |
| 50 | }; |
| 51 | expect(hasAtLeastOneProvider(allOff)).toBe(false); |
| 52 | }); |
| 53 | }); |
| 54 | |
| 55 | describe('auth-v2-workspace — Phase 8.1 two-factor / backup codes', () => { |
| 56 | test('default config has 2FA off and zero backup codes', () => { |
| 57 | const tf = twoFactorFromConfig(DEFAULT_AUTH_CONFIG); |
| 58 | expect(tf.enabled).toBe(false); |
| 59 | expect(tf.required).toBe(false); |
| 60 | expect(tf.backupCodeCount).toBe(0); |
| 61 | }); |
| 62 | |
| 63 | test('when 2FA is on, backupCodeCount is 10 (Better Auth amount)', () => { |
| 64 | const config = { |
| 65 | ...DEFAULT_AUTH_CONFIG, |
| 66 | twoFactor: { enabled: true, issuer: 'Acme', required: false }, |
| 67 | } as AuthConfig; |
| 68 | const tf = twoFactorFromConfig(config); |
| 69 | expect(tf.enabled).toBe(true); |
| 70 | expect(tf.backupCodeCount).toBe(10); |
| 71 | }); |
| 72 | |
| 73 | test('normalizeTwoFactorFlags clears required when 2FA is off', () => { |
| 74 | const tf = normalizeTwoFactorFlags({ enabled: false, required: true }); |
| 75 | expect(tf.enabled).toBe(false); |
| 76 | expect(tf.required).toBe(false); |
| 77 | expect(tf.backupCodeCount).toBe(0); |
| 78 | }); |
| 79 | |
| 80 | test('normalizeTwoFactorFlags keeps required only when enabled', () => { |
| 81 | const tf = normalizeTwoFactorFlags({ enabled: true, required: true }); |
| 82 | expect(tf.enabled).toBe(true); |
| 83 | expect(tf.required).toBe(true); |
| 84 | expect(tf.backupCodeCount).toBe(10); |
| 85 | }); |
| 86 | }); |