Skip to main content

Node.js

This guide will demonstrate how to integrate Authsignal into your Node.js app by focusing on three steps:

  1. Enrolling users with MFA
  2. Challenging users
  3. Validating challenges

You can also check out a full code example which uses the Node.js module.

Installation

Add the Authsignal Node.js module to your package.json:

npm install @authsignal/node

You can then import the module and initialize the Authsignal client, providing your secret key in the constructor:

import { Authsignal } from "@authsignal/node";

export const authsignal = new Authsignal({ secret: process.env.AUTHSIGNAL_SECRET });

You can find your secret key in the Authsignal Portal under Settings -> Api Keys.

caution

This secret key must be stored securely on your server and never exposed to the browser.

1. Enrolling users with MFA

The getUser method lets you determine if a user of your application is currently enrolled for MFA.

const { isEnrolled } = await authsignal.getUser({ userId: user.id });

if (isEnrolled) {
// The user has MFA enabled and can be challenged
} else {
// The user does not have MFA enabled
}

If the user isn't enrolled for MFA, you might want to show a button which presents them with the option to enroll via the Challenge UI. In this case, the onClick handler would make a call to your backend, which would then use the track method to initiate enrollment and return a URL to enroll the user.

const { url } = await authsignal.track({
action: "enroll",
userId: user.id,
redirectUrl: "https://example.app/settings/mfa",
});

What value you provide for action here is up to you; it simply identifies the action in the Authsignal Portal. The redirectUrl is where users will be redirected back to in your application once they leave the Challenge UI.

If the user is already enrolled for MFA, on the other hand, the url will load a page which lets them manage their existing MFA settings. If you wanted to differentiate this action from your enrollment action, for example, you could set the name of the action conditionally:

const { url } = await authsignal.track({
action: isEnrolled ? "manage-settings" : "enroll",
userId: user.id,
redirectUrl: "https://example.app/settings/mfa"
redirectToSettings: isEnrolled,
});

Enrolled users will always be prompted to complete an MFA challenge first.

Authsignal presenting a MFA challenge

If redirectToSettings is true then users will be redirected to the settings panel of the Challenge UI when the challenge is completed rather than getting immediately redirected back to your application.

Authsignal user enrollment screen which shows various authentication options

2. Challenging users

The track method can also be used to let you record other actions which your users perform and to determine if these actions need to be gated behind an MFA challenge.

For example, you might track a “withdrawal” action when a user withdraws funds from their account. Depending on what rules have been configured for this action, the result will tell you whether the user needs to be challenged or not.

const { state, url } = await authsignal.track({
userId: user.id,
action: "withdrawal",
});

if (state === "CHALLENGE_REQUIRED") {
// The user should be presented with an MFA challenge
res.redirect(url);
}

3. Validating challenges

Once the user has exited the Challenge UI and returned to your application, you should check the result of the challenge using the getAction call. The idempotencyKey param is required to identify the specific instance of the action for which the user was challenged. For example, if the challenge was presented when tracking a “withdrawal” action, the code to look up the result of that action would be:

// A token param is appended to the url when redirecting back to your app
// You can decode this token to extract the idempotency key if needed
const decodedToken = <RedirectTokenPayload>jwt.decode(req.query.token);
const { idempotencyKey } = decodedToken.other;

const { state } = await authsignal.getAction({
userId: user.id,
action: "withdrawal",
idempotencyKey,
});

if (state === "CHALLENGE_SUCCEEDED") {
// The user successfully completed the MFA challenge
// Proceed with the withdrawal
} else {
// The user did not successfully complete the MFA challenge
// Abort the withdrawal attempt
}

Further reading

For the full API description see the Server API documentation.