---
title: accounts-2fa
description: Documentation of Meteor's `accounts-2fa` package.
---

This package allows you to provide a way for your users to enable 2FA on their accounts, using an authenticator app such as Google Authenticator, or 1Password. When the user is logged in on your app, they will be able to generate a new QR code and read this code on the app they prefer. After that, they'll start receiving their codes. Then, they can finish enabling 2FA on your app, and every time they try to log in to your app, you can redirect them to a place where they can provide a code they received from the authenticator.

This package uses [node-2fa](https://www.npmjs.com/package/node-2fa) which works on top of [notp](https://github.com/guyht/notp), **that** implements TOTP ([RFC 6238](https://www.ietf.org/rfc/rfc6238.txt)) (the Authenticator standard), which is based on HOTP ([RFC 4226](https://www.ietf.org/rfc/rfc4226.txt)) to provide codes that are exactly compatible with all other Authenticator apps and services that use them.

> This package is meant to be used with [`accounts-password`](https://docs.meteor.com/api/passwords.html) or [`accounts-passwordless`](https://docs.meteor.com/packages/accounts-passwordless.html), so if you don't have either of those in your project, you'll need to add one of them. In the future, we want to enable the use of this package with other login methods, our oauth methods (Google, GitHub, etc...).

<h2 id="activating-2fa">Activating 2FA</h2>

The first step, in order to enable 2FA, is to generate a QR code so that the user can scan it in an authenticator app and start receiving codes.

{% apibox "Accounts.generate2faActivationQrCode" "module":"accounts-base" %}

Receives an `appName` which is the name of your app that will show up when the user scans the QR code. Also, a callback called with a QR code in SVG format on success or a single `Error` argument
on failure.

On success, this function will also add an object to the logged user's services object containing the QR secret:

```js
services: {
  ...
  twoFactorAuthentication: {
    secret: "***"
  }
}
``` 

Here it's an example on how to call this function:

```js
import { Buffer } from "buffer";
import { Accounts } from 'meteor/accounts-base';

--
  
const [qrCode, setQrCode] = useState(null);

--
  
<button
  onClick={() => {
    Accounts.generate2faActivationQrCode("My app name", (err, svg) => {
      if (err) {console.error("...", err);return;}
      /*
        the svg can be converted to base64, then be used like: 
         <img 
            width="200"
            src={`data:image/svg+xml;base64,${qrCode}`}
         />
      */
      setQrCode(Buffer.from(svg).toString('base64'));
    })
  }}
>
  Generate a new code
</button>
```


At this point, the 2FA won't be activated just yet. Now that the user has access to the codes generated by their authenticator app, you can call the function `Accounts.enableUser2fa`:

{% apibox "Accounts.enableUser2fa" "module":"accounts-base" %}

It should be called with a code that the users will receive from the authenticator app once they read the QR code. The callback is called with a single `Error` argument on failure. If the code provided is correct, a `type` will be added to the user's `twoFactorAuthentication` object and now 2FA is considered enabled:

```js
services: {
  ...
  twoFactorAuthentication: {
    type: "otp",
    secret: "***",
  }
}
```

<h2 id="disabling-2fa">Disabling 2FA</h2>

To disable 2FA for a user use this method:

{% apibox "Accounts.disableUser2fa" "module":"accounts-base" %}

To call this function the user must be already logged in.

<h2 id="log-in-with-2fa">Log in with 2FA</h2>

Now that you have a way to allow your users to enable 2FA on their accounts, you can create a login flow based on that.

To verify whether or not a user has 2FA enabled, you can call the function `Accounts.has2faEnabled`:

{% apibox "Accounts.has2faEnabled" "module":"accounts-base" %}

As said at the beginning of this guide, this package is currently working with two other packages: `accounts-password` and `accounts-passwordless`. Below there is an explanation on how to use this package with them.

<h3 id="working-with-accounts-password">Working with accounts-password</h3>

With the function `Accounts.has2faEnabled`, you can check whether or not the user has 2FA enabled, and based on this information, you can directly call `Meteor.loginWithPassword` if the 2FA is not enabled, or redirect the user to a place where they can provide a code, in case they do have 2FA enabled.

A way of using it would be:

```js
<button 
  onClick={() => {
    Accounts.has2faEnabled(username, (err, isEnabled) => {
      if (err) {
        console.error("Error verifying if user has 2fa enabled", err);
        return;
      }

      if (isEnabled) {
        // send user to a page or show a component 
        // where they can provide a 2FA code
        setShouldAskCode(true);
        return;
      }
      // Normal login when they don't have 2FA enabled.
      Meteor.loginWithPassword(username, password, error => {
        if (error) {
          console.error("Error trying to log in (user without 2fa)", error);
        }
      });
    });
  }
}>
  Login
</button>
```

If the user has 2FA enabled, and you try to use the function `Meteor.loginWithPassword`, the login will fail, as the user should provide a code to access the app.

The function you will need to call now to allow the user to login is `Meteor.loginWithPasswordAnd2faCode`:

{% apibox "Meteor.loginWithPasswordAnd2faCode" %}

Now you will be able to receive a code from the user and this function will verify if the code is valid. If it is, the user will be logged in.

So the call of this function should look something like this:

```js
<button onClick={() => {
  Meteor.loginWithPasswordAnd2faCode(username, password, code,error => {
    if (error) {
      console.error("Error trying to log in (user with 2fa)", error);
    }
  })}
}>
  Validate and log in
</button>
```

<h3 id="working-with-accounts-passwordless">Working with accounts-passwordless</h3>

Following the same strategy from the previous package, you can use the function `Accounts.has2faEnabled` to verify whether or not the user has 2FA enabled. If yes, you send them their token and on next step you receive their token and their 2FA code, otherwise, you still send them their token but on the next step you don't ask them for a 2FA code.

Here it's an example:

```js
Accounts.has2faEnabled(username, (err, isEnabled) => {
  if (err) {console.error("...", err);return;}
  
  Accounts.requestLoginTokenForUser({selector: "email@example.com"}, e => {
     if (e) {console.error("...", e);return;}
     
     if (isEnabled) {
      setShouldAskTokenAndCode(true);
      return;
     }

    setShouldAskToken(true);
  });
});
```

Now you can either call the standard method [`Meteor.passwordlessLoginWithToken`](https://docs.meteor.com/packages/accounts-passwordless.html#Meteor-passwordlessLoginWithToken) if they don't have 2FA enabled, or in case they do, you call the method `Meteor.passwordlessLoginWithTokenAnd2faCode` that will allow you to provide a selector, token, and 2FA code:

{% apibox "Meteor.passwordlessLoginWithTokenAnd2faCode" %}

So, using this strategy your code should look something like this:

```js
<button
  onClick={() => {
    // logging in with token and code
    if (shouldAskTokenAndCode) {
      Meteor.passwordlessLoginWithTokenAnd2faCode(
        email,
        token,
        code,
        error => {
          if (error) {console.error('...', error);}
        }
      );
      return;
    }
    // logging in just with token 
    Meteor.passwordlessLoginWithToken(
      email,
      token,
      error => {
        if (error) {console.error('...', error);}
      }
    );
  }}
>
  Validate and Log in
</button>;
```

<h2 id="integrating-auth-package">How to integrate an Authentication Package with accounts-2fa</h2>

To integrate this package with any other existing Login method, it's necessary following two steps:

1 - For the client, create a new method from your current login method. So for example, from the method `Meteor.loginWithPassword` we created a new one called `Meteor.loginWithPasswordAnd2faCode`, and the only difference between them is that the latest one receives one additional parameter, the 2FA code, but we call the same function on the server side.

2 - For the server, inside the function that will log the user in, you verify if the function `Accounts._is2faEnabledForUser` exists, and if yes, you call it providing the user you want to check if the 2FA is enabled, and if either of these statements are false, you proceed with the login flow. This function exists only when the package `accounts-2fa` is added to the project.

If both statements are true, now you verify if a code was provided, if not throw an error, if it was provided, verify if the code is valid by calling the function `Accounts._isTokenValid`, if not, throw an error.

Here it's an example:

```js
if (
    Accounts._is2faEnabledForUser &&
    Accounts._is2faEnabledForUser(user)
  ) {
    if (!code) {
      Accounts._handleError('2FA code must be informed.');
    }
    if (
      !Accounts._isTokenValid(user.services.twoFactorAuthentication.secret, code)
    ) {
      Accounts._handleError('Invalid 2FA code.');
    }
  }
  
  // continue the login flow
```
 
