Skip to main content
Skip table of contents

4. Login with Recovery Code

Login with Recovery Code

Recovery codes provide an emergency access method when you cannot use your standard Multi-Factor Authentication (MFA) methods. This scenario demonstrates how to authenticate using a recovery code when MFA is enabled on your account.

Key benefits:

  • Emergency access when standard MFA factors are unavailable

  • One-time use codes that are consumed after successful authentication

  • Alternative authentication path that still maintains security

Prerequisites

This scenario assumes:

  • 1. The user has active MFA on their account

  • 2. Recovery codes have been generated beforehand

Recovery codes must be generated in advance. Each recovery code can only be used once and is consumed after successful authentication.

Recovery codes should be stored securely. They are intended for emergency use only when you cannot access your primary MFA methods.

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

  • Instead of using standard MFA factors, we'll use a recovery code in the next step

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": "53gFlKxiYkb0DUyc8lEjtzLxbsJrr7JWgj8y1md9gpn9n8kjnGeVpWc7SLNUbfWn",
  ...
}

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: 53gFlKxiYkb0DUyc8lEjtzLxbsJrr7JWgj8y1md9gpn9n8kjnGeVpWc7SLNUbfWn

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

Step 2 - Unlock the session token using a recovery code

When you cannot access your standard MFA methods (email, SMS, or authenticator app), you can use a recovery code to unlock your session token.

Recovery codes are emergency backup codes that you should have generated and stored securely before needing them.

Each recovery code can only be used once. After successful authentication, the code is consumed and cannot be used again.

Endpoint

CODE
POST /login/recovery

Request

JSON
POST /login/recovery?token=53gFlKxiYkb0DUyc8lEjtzLxbsJrr7JWgj8y1md9gpn9n8kjnGeVpWc7SLNUbfWn

Content-Type: application/json

{
  "recoveryCode": "729435"
}

Response

JSON
{
  "remainingRecoveryCodes": 3,
  ...
}

Your session token is now unlocked using the recovery code. The recovery code has been consumed and cannot be used again.

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=53gFlKxiYkb0DUyc8lEjtzLxbsJrr7JWgj8y1md9gpn9n8kjnGeVpWc7SLNUbfWn

Content-Type: application/json

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

Response

JSON
{
  "from": 0,
  "total": 224,
  "results": [
    {
      "documentId": "01f9af91-6678-4cb0-a53b-d9ecf843e129",
      ...
    },
    ...
  ],
  "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=53gFlKxiYkb0DUyc8lEjtzLxbsJrr7JWgj8y1md9gpn9n8kjnGeVpWc7SLNUbfWn

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


Recovery Code Login Summary

Recovery codes provide an emergency authentication method when standard MFA factors are unavailable:

  • Recovery codes must be generated in advance

  • Each recovery code can only be used once and is consumed after successful authentication

  • Recovery codes should be stored securely and used only in emergency situations

Example Recovery Code 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
// Recovery Code Login flow
const URL = "https://sandbox.circularo.com";
const API_PATH = "/api/v1";

const name = "YOUR_NAME/EMAIL";
const password = "YOUR_PASSWORD";
const recoveryCode = "YOUR-RECOVERY-CODE"; // One of your pre-generated recovery codes

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 a recovery code
    if (!loginData.isUnlocked) {
        console.log('MFA required: Using recovery code for authentication');

        const recoveryResponse = await fetch(`${URL}${API_PATH}/login/recovery?token=${token}`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                recoveryCode: recoveryCode
            })
        });
        if (!recoveryResponse.ok) {
            throw new Error(`Recovery code authentication failed: ${recoveryResponse.status} ${recoveryResponse.statusText}`);
        }

        const recoveryData = await recoveryResponse.json();
        console.log(`Authentication successful. Recovery code consumed. ${recoveryData.remainingRecoveryCodes} recovery codes remaining.`);
    }

    // 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.