Skip to content

Commit df57067

Browse files
authored
Allow fallback to email mfa to skip sms setup (#323)
1 parent 7faf803 commit df57067

File tree

3 files changed

+114
-61
lines changed

3 files changed

+114
-61
lines changed

server/src/__tests__/normal/identity-mfa/email-mfa.test.ts

Lines changed: 112 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -408,63 +408,118 @@ describe(
408408
},
409409
)
410410

411-
// test(
412-
// 'could use as fallback for both sms and otp at same time',
413-
// async () => {
414-
// const mockFetch = vi.fn(async () => {
415-
// return Promise.resolve({ ok: true })
416-
// })
417-
// global.fetch = mockFetch as Mock
418-
419-
// await insertUsers(
420-
// db,
421-
// false,
422-
// )
423-
// await db.prepare('update "user" set "smsPhoneNumber" = ?, "smsPhoneNumberVerified" = ?').run(
424-
// '+16471231234',
425-
// 1,
426-
// )
427-
// db.prepare('update "user" set "mfaTypes" = ? where id = 1').run('sms,otp')
428-
429-
// const requestBody = await prepareFollowUpBody(db)
430-
// await app.request(
431-
// routeConfig.IdentityRoute.SendEmailMfa,
432-
// {
433-
// method: 'POST',
434-
// body: JSON.stringify({ ...requestBody }),
435-
// },
436-
// mock(db),
437-
// )
438-
439-
// const mfaCode = await mockedKV.get(`${adapterConfig.BaseKVKey.EmailMfaCode}-${requestBody.code}`)
440-
// expect(mfaCode?.length).toBe(6)
441-
// expect(mockFetch).toBeCalledTimes(1)
442-
443-
// global.fetch = fetchMock
444-
445-
// const res = await app.request(
446-
// routeConfig.IdentityRoute.ProcessEmailMfa,
447-
// {
448-
// method: 'POST',
449-
// body: JSON.stringify({
450-
// code: requestBody.code,
451-
// locale: requestBody.locale,
452-
// mfaCode: await mockedKV.get(`${adapterConfig.BaseKVKey.EmailMfaCode}-${requestBody.code}`),
453-
// }),
454-
// },
455-
// mock(db),
456-
// )
457-
// const json = await res.json() as { code: string }
458-
// expect(json).toStrictEqual({
459-
// code: expect.any(String),
460-
// redirectUri: 'http://localhost:3000/en/dashboard',
461-
// state: '123',
462-
// scopes: ['profile', 'openid', 'offline_access'],
463-
// })
464-
// expect(await mockedKV.get(`${adapterConfig.BaseKVKey.SmsMfaCode}-${json.code}`)).toBe('1')
465-
// expect(await mockedKV.get(`${adapterConfig.BaseKVKey.OtpMfaCode}-${json.code}`)).toBe('1')
466-
// },
467-
// )
411+
test(
412+
'could use as fallback for both sms and otp at same time',
413+
async () => {
414+
const mockFetch = vi.fn(async () => {
415+
return Promise.resolve({ ok: true })
416+
})
417+
global.fetch = mockFetch as Mock
418+
419+
await insertUsers(
420+
db,
421+
false,
422+
)
423+
await db.prepare('update "user" set "smsPhoneNumber" = ?, "smsPhoneNumberVerified" = ?').run(
424+
'+16471231234',
425+
1,
426+
)
427+
db.prepare('update "user" set "mfaTypes" = ? where id = 1').run('sms,otp')
428+
429+
const requestBody = await prepareFollowUpBody(db)
430+
await app.request(
431+
routeConfig.IdentityRoute.SendEmailMfa,
432+
{
433+
method: 'POST',
434+
body: JSON.stringify({ ...requestBody }),
435+
},
436+
mock(db),
437+
)
438+
439+
const mfaCode = await mockedKV.get(`${adapterConfig.BaseKVKey.EmailMfaCode}-${requestBody.code}`)
440+
expect(mfaCode?.length).toBe(6)
441+
expect(mockFetch).toBeCalledTimes(1)
442+
443+
global.fetch = fetchMock
444+
445+
const res = await app.request(
446+
routeConfig.IdentityRoute.ProcessEmailMfa,
447+
{
448+
method: 'POST',
449+
body: JSON.stringify({
450+
code: requestBody.code,
451+
locale: requestBody.locale,
452+
mfaCode: await mockedKV.get(`${adapterConfig.BaseKVKey.EmailMfaCode}-${requestBody.code}`),
453+
}),
454+
},
455+
mock(db),
456+
)
457+
const json = await res.json() as { code: string }
458+
expect(json).toStrictEqual({
459+
code: expect.any(String),
460+
redirectUri: 'http://localhost:3000/en/dashboard',
461+
state: '123',
462+
scopes: ['profile', 'openid', 'offline_access'],
463+
})
464+
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.SmsMfaCode}-${json.code}`)).toBe('1')
465+
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.OtpMfaCode}-${json.code}`)).toBe('1')
466+
},
467+
)
468+
469+
test(
470+
'could use as fallback for both sms and otp setup at same time',
471+
async () => {
472+
const mockFetch = vi.fn(async () => {
473+
return Promise.resolve({ ok: true })
474+
})
475+
global.fetch = mockFetch as Mock
476+
477+
await insertUsers(
478+
db,
479+
false,
480+
)
481+
482+
db.prepare('update "user" set "mfaTypes" = ? where id = 1').run('sms,otp')
483+
484+
const requestBody = await prepareFollowUpBody(db)
485+
await app.request(
486+
routeConfig.IdentityRoute.SendEmailMfa,
487+
{
488+
method: 'POST',
489+
body: JSON.stringify({ ...requestBody }),
490+
},
491+
mock(db),
492+
)
493+
494+
const mfaCode = await mockedKV.get(`${adapterConfig.BaseKVKey.EmailMfaCode}-${requestBody.code}`)
495+
expect(mfaCode?.length).toBe(6)
496+
expect(mockFetch).toBeCalledTimes(1)
497+
498+
global.fetch = fetchMock
499+
500+
const res = await app.request(
501+
routeConfig.IdentityRoute.ProcessEmailMfa,
502+
{
503+
method: 'POST',
504+
body: JSON.stringify({
505+
code: requestBody.code,
506+
locale: requestBody.locale,
507+
mfaCode: await mockedKV.get(`${adapterConfig.BaseKVKey.EmailMfaCode}-${requestBody.code}`),
508+
}),
509+
},
510+
mock(db),
511+
)
512+
const json = await res.json() as { code: string }
513+
expect(json).toStrictEqual({
514+
code: expect.any(String),
515+
redirectUri: 'http://localhost:3000/en/dashboard',
516+
state: '123',
517+
scopes: ['profile', 'openid', 'offline_access'],
518+
})
519+
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.SmsMfaCode}-${json.code}`)).toBe('1')
520+
expect(await mockedKV.get(`${adapterConfig.BaseKVKey.OtpMfaCode}-${json.code}`)).toBe('1')
521+
},
522+
)
468523

469524
test(
470525
'should throw error if auth code is wrong',

server/src/__tests__/normal/identity-mfa/sms-mfa.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe(
5353
)
5454
expect(res.status).toBe(200)
5555
expect(await res.json()).toStrictEqual({
56-
allowFallbackToEmailMfa: false,
56+
allowFallbackToEmailMfa: true,
5757
countryCode: '+1',
5858
phoneNumber: null,
5959
})
@@ -86,7 +86,7 @@ describe(
8686
)
8787
expect(res.status).toBe(200)
8888
expect(await res.json()).toStrictEqual({
89-
allowFallbackToEmailMfa: false,
89+
allowFallbackToEmailMfa: true,
9090
countryCode: '+1',
9191
phoneNumber: null,
9292
})

server/src/handlers/identity/mfa.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,6 @@ const allowSmsSwitchToEmailMfa = (
132132
c: Context<typeConfig.Context>,
133133
authCodeStore: AuthCodeBody,
134134
) => {
135-
if (!authCodeStore.user.smsPhoneNumber || !authCodeStore.user.smsPhoneNumberVerified) return false
136-
137135
const {
138136
SMS_MFA_IS_REQUIRED: enableSmsMfa,
139137
EMAIL_MFA_IS_REQUIRED: enableEmailMfa,

0 commit comments

Comments
 (0)