Skip to content

Commit

Permalink
feat: add error for missing magic link (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewJudson authored Jul 2, 2024
1 parent 771d7ef commit 85616e3
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export interface CustomErrorsOptions {
* The expired TOTP error message.
*/
expiredTotp?: string
/**
* The missing session email error message.
*/
missingSessionEmail?: string
}

authenticator.use(
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const ERRORS = {
INVALID_EMAIL: 'Email is not valid.',
INVALID_TOTP: 'Code is not valid.',
EXPIRED_TOTP: 'Code has expired.',
MISSING_SESSION_EMAIL: 'Missing email to verify. Check that same browser used for verification.',

// Miscellaneous errors.
REQUIRED_ENV_SECRET: 'Missing required .env secret.',
Expand Down
9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ export interface CustomErrorsOptions {
* The expired TOTP error message.
*/
expiredTotp?: string

/**
* The missing session email error message.
*/
missingSessionEmail?: string
}

/**
Expand Down Expand Up @@ -294,6 +299,7 @@ export class TOTPStrategy<User> extends Strategy<User, TOTPVerifyParams> {
invalidEmail: ERRORS.INVALID_EMAIL,
invalidTotp: ERRORS.INVALID_TOTP,
expiredTotp: ERRORS.EXPIRED_TOTP,
missingSessionEmail: ERRORS.MISSING_SESSION_EMAIL,
}

constructor(
Expand Down Expand Up @@ -388,7 +394,8 @@ export class TOTPStrategy<User> extends Strategy<User, TOTPVerifyParams> {

const code = formDataCode ?? this._getMagicLinkCode(request)
if (code) {
if (!sessionEmail || !sessionTotp) throw new Error(this.customErrors.expiredTotp)
if (!sessionEmail) throw new Error(this.customErrors.missingSessionEmail);
if (!sessionTotp) throw new Error(this.customErrors.expiredTotp)
await this._validateTOTP({ code, sessionTotp, session, sessionStorage, options })

const user = await this.verify({
Expand Down
35 changes: 35 additions & 0 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1051,10 +1051,45 @@ describe('[ TOTP ]', () => {
})
})

test('Should failure redirect on missing session email.', async () => {
let { session } = await setupGenerateSendTOTP()
session.unset(SESSION_KEYS.EMAIL)
const strategy = new TOTPStrategy(TOTP_STRATEGY_OPTIONS, verify)
const request = new Request('https://prodserver.com/magic-link?code=KJJERI', {
method: 'GET',
headers: {
cookie: await sessionStorage.commitSession(session),
},
})
await strategy
.authenticate(request, sessionStorage, {
...AUTH_OPTIONS,
successRedirect: '/account',
failureRedirect: '/login',
})
.catch(async (reason) => {
if (reason instanceof Response) {
expect(reason.status).toBe(302)
expect(reason.headers.get('location')).toBe(`/login`)
const session = await sessionStorage.getSession(
reason.headers.get('set-cookie') ?? '',
)
expect(session.get(AUTH_OPTIONS.sessionErrorKey)).toEqual({
message: ERRORS.MISSING_SESSION_EMAIL,
})
} else throw reason
})
})

test('Should failure redirect on stale magic-link.', async () => {
let { session } = await setupGenerateSendTOTP()
session.unset(SESSION_KEYS.TOTP)
const strategy = new TOTPStrategy(TOTP_STRATEGY_OPTIONS, verify)
const request = new Request('https://prodserver.com/magic-link?code=KJJERI', {
method: 'GET',
headers: {
cookie: await sessionStorage.commitSession(session),
},
})
await strategy
.authenticate(request, sessionStorage, {
Expand Down

0 comments on commit 85616e3

Please sign in to comment.