> ## Content Index
> Fetch the complete content index at: https://blog.fabrixly.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to Use Password + OTP (Two-Factor Authentication, 2FA) with Fabrixly-IDS
- URL: https://blog.fabrixly.com/how-to-use-password-plus-otp-2fa-3/
- Published: 2026-07-07T14:10:51.000Z
- Updated: 2026-07-07T14:38:52.000Z
- Author: Fabrixly
- Tags: Documentation

## 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 Console** → **Clients**
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**:

```bash
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`.*

```bash
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`:

```javascript
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!