Hi, we would like to add a SMS verification code to password login. IE:
user enters password
system sends them a code via SMS
user enters the code to finish logging in
We already have the capability to send SMS notifications in our application, so wondering if there is any way to integrate this into a password login flow?
I’ve looked at accounts-2fa, but feel requiring an authenticator app is too complicated for our user base.
In one of our projects, we were using a customized login and we developed a method to allow users to log in with more than one 2fa method. I will try to explain it briefly with examples below.
First of all, you can create a custom login handler using Accounts.registerLoginHandler.
You need to write a mix in for method and sub throughout the application, where you can check the 2fa validity by finding the token that the user has logged in on the connection.
Ex: mixin/UserLoginMixin
UserTypeMixin = function (methodOptions) {
const requiredUserType = methodOptions.requiredUserType
const runFunc = methodOptions.run
methodOptions.run = function () {
const user = Meteor.user()
if (!user) {
throw new Meteor.Error('unauthorized', "error message")
}
if (!user.services.loginMethodName.verificationToken.isVerified) {
throw new Meteor.Error('not-verified', "error message")
}
if (this.connection) {
const hashedToken = Accounts._getLoginToken(this.connection.id)
const token = user.services.resume.loginTokens.find((token) => token.hashedToken === hashedToken)
if (!token?.isVerified) {
throw new Meteor.Error('_2fa-not-verified', "error message")
}
}
return runFunc.call(this, ...arguments)
}
return methodOptions
}
I think you can add some codes to make accounts-2fa works with SMS. I will write my idea here so I can do it later
On client side, at the page you display the QR code, you can add a textbox allows user to enter their phone number.
When user submit their phone number, you call a method on the server, let’s say 2fa.registerPhoneNumber. This method need to do 3 things:
Check if 2fa enabled, throw error. This method should work only 2fa NOT enabled.
Add/Update that number to services.twoFactorAuthentication. Let’s say services.twoFactorAuthentication.phoneNumber
Fetch user’s secret then call Accounts._generate2faToken(secret) to generate token and send this token to user’s phone number.
When 2fa enabled and user login. We will need a button to trigger a method call to send SMS to registered phone number. Let’s say 2fa.smsToken. This method need to do these things:
Check if 2fa enabled for this user
Fetch user’s secret then call Accounts._generate2faToken(secret) to generate token and send this token to user’s registered phone number.
If you want to make an option that let user change their phone number.
You can create a form which has 2 steps:
Step 1: A textbox to enter new phone number and submit button which calls a method, let’s say 2fa.changePhoneNumber
Step 2: A textbox to enter token and submit button which calls a method, let’s say 2fa.verifyPhoneNumber
2fa.changePhoneNumber: This method will
Check if 2fa enabled for this user
Set services.twoFactorAuthentication.newPhoneNumber to submitted new number.
Generate and send a token to this number
2fa.verifyPhoneNumber: This method will
Check if 2fa enabled for this user
Verify token
Set services.twoFactorAuthentication.phoneNumber to newPhoneNumber and newPhoneNumber to null or undefined.
The easiest route will be forking the current 2FA implementation to send the OTP through SMS instead of displaying the authenticator QR code. And maybe adjusting the OTP validity applicable for SMS.