Create the same integration with 2fa inside accounts-passwordless

This commit is contained in:
denihs
2022-01-25 11:28:49 -04:00
parent 374b0198a3
commit 4278732df8
6 changed files with 68 additions and 20 deletions

View File

@@ -43,7 +43,7 @@ const [qrCode, setQrCode] = useState(null);
<button
onClick={() => {
Accounts.generate2faActivationQrCode((err, svg) => {
Accounts.generate2faActivationQrCode("My app name", (err, svg) => {
if (err) {console.error("...", err);return;}
/*
the svg can be converted to base64, then be used like:

View File

@@ -10,7 +10,7 @@ Npm.depends({
});
Package.onUse(function(api) {
api.use(['accounts-base', 'accounts-password'], ['client', 'server']);
api.use(['accounts-base'], ['client', 'server']);
// Export Accounts (etc) to packages using this one.
api.imply('accounts-base', ['client', 'server']);

View File

@@ -188,12 +188,12 @@ Accounts.registerLoginHandler("password", options => {
Accounts._is2faEnabledForUser(options.user)
) {
if (!options.code) {
Accounts._handleError('Token must be informed.');
Accounts._handleError('2FA code must be informed.');
}
if (
!Accounts._isTokenValid(user.services.twoFactorAuthentication.secret, options.code)
) {
Accounts._handleError('Invalid token.');
Accounts._handleError('Invalid 2FA code.');
}
}

View File

@@ -21,6 +21,31 @@ const transformSelector = selector => {
return { username: selector };
};
const internalPasswordlessLoginWithToken = ({
selector,
token,
code,
callback,
}) => {
Accounts.callLoginMethod({
methodArguments: [
{
selector: transformSelector(selector),
token,
code,
},
],
userCallback: error => {
if (error) {
reportError(error, callback);
} else {
callback && callback();
}
},
});
};
// Attempt to log in with a token.
//
// @param selector {String|Object} One of the following:
@@ -31,6 +56,7 @@ const transformSelector = selector => {
// @param password {String}
// @param callback {Function(error|undefined)}
/**
* @summary Log the user in with a one time token.
* @locus Client
@@ -42,21 +68,22 @@ const transformSelector = selector => {
* @importFromPackage meteor
*/
Meteor.passwordlessLoginWithToken = (selector, token, callback) => {
Accounts.callLoginMethod({
methodArguments: [
{
selector: transformSelector(selector),
token,
},
],
userCallback: error => {
if (error) {
reportError(error, callback);
} else {
callback && callback();
}
},
});
internalPasswordlessLoginWithToken({ selector, token, callback });
};
/**
* @summary Log the user in with a one time token.
* @locus Client
* @param {Object|String} selector Username, email or custom selector to identify the user.
* @param {String} token one time token generated by the server
* @param {String} code generated by the user's authenticator app
* @param {Function} [callback] Optional callback.
* Called with no arguments on success, or with a single `Error` argument
* on failure.
* @importFromPackage meteor
*/
Meteor.passwordlessLoginWithTokenAnd2faCode = (selector, token, code, callback) => {
internalPasswordlessLoginWithToken({ selector, token, code, callback });
};
/**

View File

@@ -1,5 +1,5 @@
import { Accounts } from 'meteor/accounts-base';
import { getUserById, tokenValidator } from './server_utils';
import {getUserById, NonEmptyString, tokenValidator} from './server_utils';
import { Random } from 'meteor/random';
const ONE_HOUR_IN_MILLISECONDS = 60 * 60 * 1000;
@@ -63,6 +63,7 @@ Accounts.registerLoginHandler('passwordless', options => {
check(options, {
token: tokenValidator(),
code: Match.Optional(NonEmptyString),
selector: Accounts._userQueryValidator,
});
@@ -79,6 +80,21 @@ Accounts.registerLoginHandler('passwordless', options => {
Accounts._handleError('User has no token set');
}
// This method is added by the package accounts-2fa
if (
Accounts._is2faEnabledForUser &&
Accounts._is2faEnabledForUser(user)
) {
if (!options.code) {
Accounts._handleError('2FA code must be informed.');
}
if (
!Accounts._isTokenValid(user.services.twoFactorAuthentication.secret, options.code)
) {
Accounts._handleError('Invalid 2FA code.');
}
}
const result = checkToken({
user,
selector,

View File

@@ -9,3 +9,8 @@ export const tokenValidator = () => {
str => Match.test(str, String) && str.length <= tokenLength
);
};
export const NonEmptyString = Match.Where(x => {
check(x, String);
return x.length > 0;
});