Skip to main content
Skip table of contents

3. Login with MFA

Multi-Factor Authentication (MFA) Login

Multi-Factor Authentication (MFA) provides an additional layer of security for your Circularo account by requiring a second verification step after entering your password. This scenario demonstrates how to authenticate when MFA is enabled on your account.

Key benefits:

  • Enhanced security through multi-factor verification

  • Protection against unauthorized access even if your password is compromised

  • Support for multiple authentication factors including email, SMS, and authenticator apps

Prerequisites

This scenario assumes the user has active MFA on their account. Users can activate MFA either themselves using the Preferences -> Security settings, or it can be enforced by an administrator.

When MFA is enabled, you'll need to complete both authentication steps before you can access protected resources.

Step 1 - Login with MFA-enabled account

Authenticate with your username and password to obtain a session token. Since your account has MFA enabled, the token will be returned in a locked state.

  • This initial authentication verifies your first factor (something you know - your password)

  • The returned token will be locked and requires additional verification

  • You'll need to complete the MFA challenge before you can use the token

Endpoint

CODE
POST /login

Request

JSON
POST /login

Content-Type: application/json

{
  "name": "mary.griffin@circularo.com",
  "password": "#32Password1!",
  "returnToken": true
}

Response

JSON
{
  "logged": true,
  "isUnlocked": false,
  "token": "L9Gyrr8JswCjh6Vm4iDMrKt1kDOKLyWwbFrWPemRUYZBQiFqzCIYjVpeep7gI9RP",
  ...
}

The response contains the following important properties:

  • token - Located at token in the response.

    • The authentication token that should be included in subsequent API requests. This token identifies your session and provides access to authorized resources.

    • Example value: L9Gyrr8JswCjh6Vm4iDMrKt1kDOKLyWwbFrWPemRUYZBQiFqzCIYjVpeep7gI9RP

Note: The token is locked initially. To access other endpoints, unlocking is required.

Step 2 - Unlock the session token

In the previous step, MFA was invoked and dispatched. Assuming the "email" was configured as the MFA factor in the user account, an email containing the verification code was sent.

Utilize the received code in this call to unlock your session token.

Other MFA provider options include "sms" for text message verification and "totp" for time-based one-time passwords from authenticator apps.

Endpoint

CODE
POST /login/unlock

Request

JSON
POST /login/unlock?token=L9Gyrr8JswCjh6Vm4iDMrKt1kDOKLyWwbFrWPemRUYZBQiFqzCIYjVpeep7gI9RP

Content-Type: application/json

{
  "factors": [
    {
      "factor": "email",  //MFA factor, other options are \"sms\" and \"totp\"
      "password": "271008"
    }
  ]
}

Response

JSON
{
  "isUnlocked": true,
  "factors": [
    {
      "factor": "email",
      "unlocked": true,
      ...
    }
  ]
}

Your session token is now unlocked, allowing you to access protected resources and API endpoints.

Step 3 - Test the session token by retrieving available documents (Optional)

This endpoint demonstrates how to use your unlocked session token to perform an actual operation - in this case, searching for available documents in the system.

  • The session token is passed in the query parameters

  • The search operation returns documents the authenticated user has access to

  • This pattern applies to all token protected API endpoints - include your session token in each request

This is a practical example of how to use your session token for API operations. Do not call this endpoint if you don't wish to perform a search operation.

Endpoint

CODE
POST /search

Request

JSON
POST /search?token=L9Gyrr8JswCjh6Vm4iDMrKt1kDOKLyWwbFrWPemRUYZBQiFqzCIYjVpeep7gI9RP

Content-Type: application/json

{
  "from": 0,
  "size": 10
}

Response

JSON
{
  "from": 0,
  "total": 224,
  "results": [
    {
      "documentId": "cac05412-ca16-449f-b472-bdc5d8202b7d",
      ...
    },
    ...
  ],
  "users": {
    ...
  },
  ...
}

The search operation was successful, demonstrating that your session token is now fully unlocked and working correctly.

The response includes a list of documents that the authenticated user has access to.

Step 4 - Terminate the session (Optional)

Invalidates the current session token, effectively logging the user out of the system. This is an important security practice to prevent unauthorized access after a session is no longer needed.

  • Invalidates the token provided in the request

  • Prevents the token from being used for any future API requests

  • Should be called when a session is no longer needed

  • Does not affect other active sessions for the same user

This endpoint only invalidates the specific token provided in the request. Other active sessions for the same user will remain valid.

Endpoint

CODE
GET /logout

Request

JSON
GET /logout?token=L9Gyrr8JswCjh6Vm4iDMrKt1kDOKLyWwbFrWPemRUYZBQiFqzCIYjVpeep7gI9RP

The session has been successfully terminated. The token is now invalid and cannot be used for further API requests.


MFA Login Summary

Multi-Factor Authentication provides enhanced security for your Circularo account:

  • Initial authentication requires your username and password

  • A second factor verification is required to unlock the session token

  • Once unlocked, the session token functions identically to a regular session token

  • Session tokens remain valid for 30 days by default after being unlocked

Example MFA Login Flow

See our OpenAPI documentation to learn about the full set of API endpoints and parameters.

Please use proper exception handling and function decomposition in your own code. The code is provided for illustrative purposes only and is not intended for production use.

JAVASCRIPT
// MFA Login flow
const URL = "https://sandbox.circularo.com";
const API_PATH = "/api/v1";

const name = "YOUR_NAME/EMAIL";
const password = "YOUR_PASSWORD";

let token;

try {
    // Step 1: Login to get locked session token
    const loginResponse = await fetch(`${URL}${API_PATH}/login`, {
        method: 'POST',
        body: JSON.stringify({ name, password, returnToken: true }),
        headers: { 'Content-Type': 'application/json' }
    });
    if (!loginResponse.ok) {
        throw new Error(`Login failed: ${loginResponse.status} ${loginResponse.statusText}`);
    }

    const loginData = await loginResponse.json();
    if (!loginData.logged) {
        throw new Error('Authentication failed');
    }
    token = loginData.token;

    // Step 2: Unlock the token with MFA
    if (!loginData.isUnlocked) {
        console.log('MFA required: Token is locked and requires additional authentication');

        // Obtain the MFA code from an email / SMS / authenticator app
        const mfaCode = getMfaCode();

        const unlockResponse = await fetch(`${URL}${API_PATH}/login/unlock?token=${token}`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                factors: [{
                    factor: "email", // or "sms" or "totp"
                    password: mfaCode
                }]
            })
        });
        if (!unlockResponse.ok) {
            throw new Error(`Unlock request failed: ${unlockResponse.status} ${unlockResponse.statusText}`);
        }

        const unlockData = await unlockResponse.json();
        if (!unlockResponse.isUnlocked) {
            //Token still not unlocked - there may be more factors to unlock, see the response for details
            throw new Error(`Token not unlocked`);
        } else {
            //Token is unlocked and ready to be used now
        }
    }

    // Step 3: Use the token for operations (illustrative purpose)
    const searchResponse = await fetch(`${URL}${API_PATH}/search?token=${token}`, {
        method: 'POST',
        body: JSON.stringify({ from: 0, size: 10 }),
        headers: { 'Content-Type': 'application/json' }
    });
    if (!searchResponse.ok) {
        throw new Error(`Search failed: ${searchResponse.status} ${searchResponse.statusText}`);
    }
    const searchResults = await searchResponse.json();

    // Step 4: Logout when done
    const logoutResponse = await fetch(`${URL}${API_PATH}/logout?token=${token}`);
    if (!logoutResponse.ok) {
        throw new Error(`Logout failed: ${logoutResponse.status} ${logoutResponse.statusText}`);
    }
    console.log('Successfully logged out');

} catch (error) {
    console.error('Error in authentication flow:', error.message);

    // Always try to logout if we have a token, even if other operations failed
    if (token) {
        try {
            const logoutResponse = await fetch(`${URL}${API_PATH}/logout?token=${token}`);
            if (!logoutResponse.ok) {
                throw new Error(`Logout failed: ${logoutResponse.status} ${logoutResponse.statusText}`);
            }
        } catch (logoutError) {
            console.error('Failed to logout:', logoutError.message);
        }
    }
}
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.