in packages/fxa-content-server/app/scripts/views/mixins/signin-mixin.js [33:144]
signIn(account, password, options = {}) {
if (
!account ||
account.isDefault() ||
(!account.has('sessionToken') && !password)
) {
return Promise.reject(AuthErrors.toError('UNEXPECTED_ERROR'));
}
return this.invokeBrokerMethod('beforeSignIn', account)
.then(() => {
// Always pass `signin` for viewName regardless of the actual view
// because we want to log the real action that is being performed.
// This is important for the infamous signin-from-signup feature.
this.logFlowEvent('attempt', 'signin');
let verificationMethod =
account.get('verificationMethod') || VerificationMethods.EMAIL_2FA;
// Check to see if this is an oauth client is requesting 2FA.
// If it is, set/override the corresponding verificationMethod.
// Login requests that ask for 2FA but don't have it setup on their account
// will return an error.
if (this.relier.isOAuth() && this.relier.wantsTwoStepAuthentication()) {
verificationMethod = VerificationMethods.TOTP_2FA;
}
// Some brokers (e.g. Sync) hand off control of the sessionToken, and hence expect
// each signin to generate a fresh token. Make sure that will happen.
if (
account.has('sessionToken') &&
!this.broker.hasCapability('reuseExistingSession')
) {
account.discardSessionToken();
}
return this.user.signInAccount(account, password, this.relier, {
// a resume token is passed in to allow
// unverified account or session users to complete
// email verification.
resume: this.getStringifiedResumeToken(account),
unblockCode: options.unblockCode,
verificationMethod: verificationMethod,
});
})
.then((account) => {
if (this.formPrefill) {
this.formPrefill.clear();
}
if (this.relier.accountNeedsPermissions(account)) {
return this.navigate('signin_permissions', {
account: account,
// the permissions screen will call onSubmitComplete
// with an updated account
onSubmitComplete: this.onSignInSuccess.bind(this),
});
}
if (this.relier.shouldOfferToSync(this.viewName)) {
// flows that are a part of the 'browser' relier which
// do not pass a service get asked of they want to Sync
// force_auth attempts do not a choice for Sync
return this.navigate('would_you_like_to_sync', {
account: account,
// ask the user if they want to sync, but don't ask to specify data choices via CWTS
// see https://github.com/mozilla/fxa/issues/3083 for details
skipCWTS: true,
// propagate the onSubmitComplete to choose_what_to_sync screen if needed
onSubmitComplete: this.onSignInSuccess.bind(this),
});
}
if (typeof options.onSuccess === 'function') {
options.onSuccess();
}
return this.onSignInSuccess(account);
})
.catch((err) => {
if (
AuthErrors.is(err, 'THROTTLED') ||
AuthErrors.is(err, 'REQUEST_BLOCKED')
) {
return this.onSignInBlocked(account, password, err);
}
if (
AuthErrors.is(err, 'EMAIL_HARD_BOUNCE') ||
AuthErrors.is(err, 'EMAIL_SENT_COMPLAINT')
) {
return this.navigate('signin_bounced', {
email: account.get('email'),
});
}
if (
AuthErrors.is(err, 'TOTP_REQUIRED') ||
AuthErrors.is(err, 'INSUFFICIENT_ACR_VALUES') ||
OAuthErrors.is(err, 'MISMATCH_ACR_VALUES')
) {
return this.navigate('inline_totp_setup', {
account: account,
onSubmitComplete: this.onSignInSuccess.bind(this),
});
}
// re-throw error, it'll be handled elsewhere.
throw err;
});
},