How to Use Password + OTP (Two-Factor Authentication, 2FA) with Fabrixly-IDS

Overview

Two-Factor Authentication (2FA) adds an extra layer of security: users enter a password and an OTP to log in! This mode is called password_and_otp in Fabrixly-IDS.

Step 1: Configure 2FA for Your Client

First, set up your client in Fabrixly-IDS to use 2FA:

Admin Console Setup

  1. Go to Fabrixly-IDS Admin ConsoleClients
  2. Select your client → click Edit
  3. Under Authentication Settings:
    • Auth Mode: select Password + OTP
    • Enable Mobile Login: toggle on/off
    • OTP Delivery Method: choose email/SMS/both
  4. Save!

API Setup

Authenticating API Requests

Management API endpoints (like /api/clients, /api/users, etc.) require administrative authentication. To call them:

  1. Include the Token in the Authorization Header: Add -H "Authorization: Bearer <your-admin-jwt-token>" to your API calls.

Obtain an Admin JWT Token:

curl -X POST https://ids.fabrixly.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@example.com",
    "password": "your-admin-password"
  }'

Note: This returns a JSON response containing a token.

curl -X POST https://ids.fabrixly.com/api/clients \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <your-admin-jwt-token>" \
  -d '{
    "client_name": "2FA App",
    "redirect_uris": ["http://localhost:3004/callback"],
    "response_types": ["code"],
    "grant_types": ["authorization_code", "refresh_token"],
    "token_endpoint_auth_method": "client_secret_basic",
    "auth_mode": "password_and_otp",
    "otp_delivery_method": "email"
  }'

User Login Flow

  1. User visits your app, clicks "Login"
  2. Redirected to Fabrixly-IDS login screen:
    • Step 1: Enter email/mobile number and password → click "Continue"
    • Step 2: Receive OTP (via email/SMS) → enter OTP → click "Verify & Log In"
  3. Fabrixly-IDS validates password and OTP
  4. User is redirected back to your app with auth code
  5. App exchanges code for tokens and logs user in!

Example 2FA Express App

Create 2fa-app.js:

const express = require('express');
const session = require('express-session');
const { Issuer, generators } = require('openid-client');

const app = express();
const PORT = 3004;

app.use(session({
  secret: '2fa-is-super-secure',
  resave: false,
  saveUninitialized: true
}));

async function init() {
  const issuer = await Issuer.discover('https://ids.fabrixly.com/oidc');
  return new issuer.Client({
    client_id: 'your-2fa-client-id',
    client_secret: 'your-2fa-client-secret',
    redirect_uris: ['http://localhost:3004/callback']
  });
}

let client;
init().then(c => client = c);

app.get('/', (req, res) => {
  if (req.session.user) {
    res.redirect('/dashboard');
  } else {
    res.send(`
      <h1>2FA Example App</h1>
      <a href="/login">Login with 2FA</a>
    `);
  }
});

app.get('/login', (req, res) => {
  req.session.state = generators.state();
  req.session.nonce = generators.nonce();
  
  const authUrl = client.authorizationUrl({
    scope: 'openid profile email',
    state: req.session.state,
    nonce: req.session.nonce
  });
  res.redirect(authUrl);
});

app.get('/callback', async (req, res) => {
  const params = client.callbackParams(req);
  const tokenSet = await client.callback(
    'http://localhost:3004/callback',
    params,
    { state: req.session.state, nonce: req.session.nonce }
  );
  req.session.tokenSet = tokenSet;
  req.session.user = await client.userinfo(tokenSet.access_token);
  res.redirect('/dashboard');
});

app.get('/dashboard', (req, res) => {
  if (!req.session.user) return res.redirect('/');
  
  res.send(`
    <h1>2FA Protected Dashboard!</h1>
    <p>Welcome, ${req.session.user.email}!</p>
    <p>You logged in with password + OTP 🔐</p>
    <a href="/">Home</a>
  `);
});

app.listen(PORT, () => console.log(`2FA app running on https://ids.fabrixly.com:${PORT}`));

Why Use 2FA?

  1. Higher Security: Even if a password is compromised, attackers still need the OTP
  2. Easy for Users: OTPs are sent to email or SMS, no extra hardware/app needed
  3. Configurable: Choose delivery methods that work best for your users

Security Best Practices

  1. Use HTTPS in production!
  2. Keep OTP expiry short (default: 5 minutes)
  3. Use short OTP length (6 digits is good)
  4. Enable account locking after too many failed attempts
  5. Educate users to not share OTPs!

Troubleshooting

  • Password correct but login fails: Check that OTP is still valid and correct
  • OTP not arriving: Check spam (email) or verify SMS settings
  • Can't switch steps: Ensure auth_mode is set to password_and_otp in client config

Next Steps

  • Try magic links for even simpler passwordless login!
  • Explore social logins!

Subscribe to The Fabrixly Blog

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe