mirror of
https://github.com/meteor/meteor.git
synced 2026-01-11 00:28:02 -05:00
Create the same integration with 2fa inside accounts-passwordless
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 });
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user