passwordless.test.ts86 lines · main
1import { describe, expect, test } from 'bun:test';
2
3import {
4 hashSecret,
5 isMagicLinkBaseAllowed,
6 matchPasswordlessSecret,
7 pickMagicLinkAppOrigin,
8 sixDigitCode,
9} from './passwordless.js';
10
11describe('passwordless pure helpers (Phase 3)', () => {
12 test('pickMagicLinkAppOrigin prefers https production over localhost', () => {
13 expect(
14 pickMagicLinkAppOrigin(
15 ['http://localhost:3000', 'https://pay.mavifinans.sh'],
16 null,
17 ),
18 ).toBe('https://pay.mavifinans.sh');
19 });
20
21 test('pickMagicLinkAppOrigin prefers matching request Origin', () => {
22 expect(
23 pickMagicLinkAppOrigin(
24 ['http://localhost:3000', 'https://pay.mavifinans.sh'],
25 'http://localhost:3000',
26 ),
27 ).toBe('http://localhost:3000');
28 });
29
30 test('sixDigitCode is 6 digits', () => {
31 for (let i = 0; i < 20; i++) {
32 const c = sixDigitCode();
33 expect(c).toMatch(/^\d{6}$/);
34 expect(Number(c)).toBeGreaterThanOrEqual(100000);
35 expect(Number(c)).toBeLessThanOrEqual(999999);
36 }
37 });
38
39 test('match OTP-only hash', () => {
40 const otp = '123456';
41 const stored = hashSecret(otp);
42 expect(matchPasswordlessSecret(stored, { userInputCode: otp })).toBe(true);
43 expect(matchPasswordlessSecret(stored, { userInputCode: '000000' })).toBe(
44 false,
45 );
46 });
47
48 test('match link-only hash', () => {
49 const link = 'abcLinkCodeXYZ';
50 const stored = hashSecret(link);
51 expect(matchPasswordlessSecret(stored, { linkCode: link })).toBe(true);
52 expect(matchPasswordlessSecret(stored, { linkCode: 'nope' })).toBe(false);
53 });
54
55 test('match dual otp:link form', () => {
56 const otp = '654321';
57 const link = 'linkSecret99';
58 const stored = `${hashSecret(otp)}:${hashSecret(link)}`;
59 expect(matchPasswordlessSecret(stored, { userInputCode: otp })).toBe(true);
60 expect(matchPasswordlessSecret(stored, { linkCode: link })).toBe(true);
61 expect(
62 matchPasswordlessSecret(stored, { userInputCode: '111111', linkCode: 'x' }),
63 ).toBe(false);
64 });
65
66 test('empty input fails', () => {
67 expect(matchPasswordlessSecret(hashSecret('1'), {})).toBe(false);
68 });
69
70 test('isMagicLinkBaseAllowed rejects evil origins when allowlist set', () => {
71 expect(
72 isMagicLinkBaseAllowed(
73 'https://evil.example/phish',
74 ['https://pay.mavifinans.sh'],
75 'https://pay.mavifinans.sh',
76 ),
77 ).toBe(false);
78 expect(
79 isMagicLinkBaseAllowed(
80 'https://pay.mavifinans.sh/auth/verify',
81 ['https://pay.mavifinans.sh'],
82 'https://pay.mavifinans.sh',
83 ),
84 ).toBe(true);
85 });
86});