Accounts-Phone
Phone and OTP based login for Meteor.
Installation
In a Meteor app directory, enter:
$ meteor add settlin:accounts-phone
The database
In accordance with the current emails
field used by accounts-password
, we use phones
field in the user document: {phones: [{number, verified: false}]}
. The otp is stored in services.phone.otp
.
The flow
Use a simple Meteor method,
1function sendOtpViaSms(otp) {.....} // the function through which you send sms 2 3Meteor.methods({ 4 sendOtpForLogin: function(to) { 5 if (Meteor.isClient) return null; 6 7 // otp must be generated on the server and never revealed to the client 8 check(to, String); 9 10 let user = Meteor.users.findOne({'phones.number': to}); 11 12 // if there is no user with the given phone number, we create a new one. 13 // Accounts.createUser is available only on the server and creates a new user with two fields: `phones` and `services`. It ensures that the phone numbers are always unique for users. 14 if (!user) user = {_id: Accounts.createUserWithPhone({phone: to})}; 15 16 // send otp as sms 17 let otp = Math.round(Random.fraction() * 100000); 18 19 // Accounts.setPhoneOtp sets the otp in the `__otps` collection: {phone, otp, purpose: '__login__'}. 20 Accounts.setPhoneOtp(user._id, otp); 21 }, 22});
Use this method to send otp whenever needed. Next, take the otp from the user and call,
Meteor.loginWithPhone({phone, otp}, callback);
This method works as any other Meteor.loginWith<Service>
method.
Simple API
Server
1Meteor.otps; // the collection that contains otps in the form {phone, otp, purpose, createdAt} with an index created by: Meteor.otps._ensureIndex({phone: 1, purpose: 1}, {unique: true, name: 'phoneAndPurpose'}); 2// not available on client 3 4/** 5 * @summary Set the otp for a user. 6 * @locus Server 7 * @param {String} userId The id of the user to update. 8 * @param {String} otp OTP 9 * @returns {Void} null 10 */ 11Accounts.setPhoneOtp = function(userId, otp) {...}; 12 13/** 14 * @summary Add a phone number for a user. Use this instead of directly 15 * updating the database. The operation will fail if there is a different user 16 * with same phone. 17 * @locus Server 18 * @param {String} userId The ID of the user to update. 19 * @param {String} newPhone A new phone number for the user. 20 * @param {Boolean} [verified] Optional - whether the new phone number should 21 * be marked as verified. Defaults to false. 22 * @returns {Void} null 23 */ 24Accounts.addPhone = function(userId, newPhone, verified) {...}; 25 26/** 27 * @summary Remove an phone number for a user. Use this instead of updating 28 * the database directly. 29 * @locus Server 30 * @param {String} userId The ID of the user to update. 31 * @param {String} phone The phone number to remove. 32 * @returns {Void} null 33 */ 34Accounts.removePhone = function(userId, phone) {...}; 35 36/** 37 * @summary Create a user directly on the server. Unlike the client version, this does not log you in as this user after creation. 38 * @locus Server 39 * @param {Object} options Object with arguments. Needs just {phone: String} for now. 40 * @returns {String} userId The newly created user's _id 41 */ 42Accounts.createUserWithPhone = function(options) {...}; 43 44/** 45 * @summary finds user by doing a phone number search. Throws error if multiple found. 46 * @param {String} phone phone number. 47 * @return {Object} user document 48 */ 49Accounts.findUserByPhone = function(phone) {...};
Client
1/** 2 * @summary Log the user in with a password. 3 * @locus Client 4 * @param {Object} options phone and otp 5 * @param {Function} [callback] Optional callback. Called with no arguments on success, 6 * or with a single `Error` argument on failure. 7 * @return {Void} null 8 */ 9Meteor.loginWithPhone = function(options, callback) {...};
both
1/** 2 * @summary Log the user in with a password. 3 * @locus Both 4 * @param {String} phone phone number 5 * @return {String} sanitized phone number. Tweaked for Indian numbers, but works for other countries as well. Uses https://github.com/settlin/phoneparser.git 6 */ 7 8Accounts.sanitizePhone = function(phone) {...};
If you need a phone + password login, use https://github.com/okland/accounts-phone.