in packages/fxa-auth-server/lib/routes/recovery-phone.ts [270:431]
async confirmCode(request: AuthRequest, isSetup: boolean) {
const {
id: sessionTokenId,
uid,
email,
} = request.auth.credentials as SessionTokenAuthCredential;
const { code } = request.payload as unknown as {
code: string;
};
if (!email) {
throw AppError.invalidToken();
}
await this.customs.checkAuthenticated(
request,
uid,
'verifyRecoveryPhoneTotpCode'
);
let success = false;
try {
if (isSetup) {
// This is the initial setup case, where a user is validating an sms
// code on their phone for the first time. It does NOT impact the totp
// token's database state.
success = await this.recoveryPhoneService.confirmSetupCode(uid, code);
} else {
// This is a sign in attempt. This will check the code, and if valid, mark the
// session token verified. This session will have a security level that allows
// the user to remove totp devices.
success = await this.recoveryPhoneService.confirmSigninCode(uid, code);
// Mark session as verified
if (success) {
await this.accountManager.verifySession(
uid,
sessionTokenId,
VerificationMethods.sms2fa
);
}
}
} catch (error) {
if (error instanceof RecoveryPhoneNotEnabled) {
throw AppError.featureNotEnabled();
}
if (error instanceof RecoveryNumberAlreadyExistsError) {
throw AppError.recoveryPhoneNumberAlreadyExists();
}
if (error instanceof RecoveryPhoneRegistrationLimitReached) {
throw AppError.recoveryPhoneRegistrationLimitReached();
}
throw AppError.backendServiceFailure(
'RecoveryPhoneService',
'confirmCode',
{ uid },
error
);
}
if (success) {
await this.glean.twoStepAuthPhoneCode.complete(request);
const account = await this.db.account(uid);
const { acceptLanguage, geo, ua } = request.app;
if (isSetup) {
this.statsd.increment('account.recoveryPhone.phoneAdded.success');
try {
const { phoneNumber, nationalFormat } =
// User has successfully set up a recovery phone. Give back the
// full nationalFormat (don't strip it).
await this.recoveryPhoneService.hasConfirmed(uid);
await this.mailer.sendPostAddRecoveryPhoneEmail(
account.emails,
account,
{
acceptLanguage,
maskedLastFourPhoneNumber: `••••••${this.recoveryPhoneService.stripPhoneNumber(
phoneNumber || '',
4
)}`,
timeZone: geo.timeZone,
uaBrowser: ua.browser,
uaBrowserVersion: ua.browserVersion,
uaOS: ua.os,
uaOSVersion: ua.osVersion,
uaDeviceType: ua.deviceType,
uid,
}
);
recordSecurityEvent('account.recovery_phone_setup_complete', {
db: this.db,
request,
});
return {
phoneNumber,
nationalFormat,
status: RecoveryPhoneStatus.SUCCESS,
};
} catch (error) {
// log email send error but don't throw
// user should be allowed to proceed
this.log.trace('account.recoveryPhone.phoneAddedNotification.error', {
error,
});
}
} else {
this.statsd.increment('account.recoveryPhone.phoneSignin.success');
// this signals the end of the login flow
await request.emitMetricsEvent('account.confirmed', { uid });
recordSecurityEvent('account.recovery_phone_signin_complete', {
db: this.db,
request,
});
try {
await this.mailer.sendPostSigninRecoveryPhoneEmail(
account.emails,
account,
{
acceptLanguage,
timeZone: geo.timeZone,
uaBrowser: ua.browser,
uaBrowserVersion: ua.browserVersion,
uaOS: ua.os,
uaOSVersion: ua.osVersion,
uaDeviceType: ua.deviceType,
uid,
}
);
} catch (error) {
// log email send error but don't throw
// user should be allowed to proceed
this.log.trace(
'account.recoveryPhone.phoneSigninNotification.error',
{
error,
}
);
}
}
return { status: RecoveryPhoneStatus.SUCCESS };
}
recordSecurityEvent('account.recovery_phone_signin_failed', {
db: this.db,
request,
});
throw AppError.invalidOrExpiredOtpCode();
}