Authentication

Authentication

The Infomaxim API uses JWT (JSON Web Tokens) for authentication. All protected endpoints require a valid JWT access token in the Authorization header.

Authentication Flow

  1. Login with credentials to receive access and refresh tokens
  2. Include access token in Authorization header for subsequent requests
  3. When access token expires (1 hour), use refresh token to obtain a new access token
  4. Refresh tokens are valid for 30 days

Authentication Headers

Authorization: Bearer <your_jwt_access_token>
Content-Type: application/json

POST/auth/login

Authenticates a user and returns JWT tokens for API access.

Business Logic

  1. Validates email and password format
  2. Uses Passport local strategy to authenticate user credentials
  3. Searches for user in aurora_users or specified authTable
  4. Verifies password using bcrypt (upgrades legacy encrypted passwords automatically)
  5. Generates JWT access token (valid for 1 hour) and refresh token (valid for 30 days)
  6. Returns user profile data along with tokens

Request Body

{
  "email": "user@example.com",
  "password": "SecurePass123!",
  "appID": 1,
  "authTable": "aurora_users"
}

Parameters

  • email string required

    User's email address. Must be a valid email format.

  • password string required

    User's password. Minimum 8 characters.

  • appID number optional

    Application ID for the Infomaxim instance. Optional but recommended.

  • authTable string optional

    Custom authentication table name. Defaults to 'aurora_users'.

Success Response (200 OK)

{
  "status": "Success",
  "token": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
      "id": 123,
      "email": "user@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "role": "admin"
    }
  }
}

Error Responses

// 401 Unauthorized - Invalid credentials
{
  "status": "Failed",
  "message": "Invalid credentials"
}

// 401 Unauthorized - Authentication failed
{
  "status": "Failed",
  "message": "Authentication failed"
}

// 400 Bad Request - Validation error
{
  "status": "Failed",
  "message": "Validation error",
  "errors": [
    {
      "field": "email",
      "message": "Please provide a valid email address"
    }
  ]
}

Example Usage

// JavaScript Example
const response = await fetch('http://localhost:3001/auth/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: 'user@example.com',
    password: 'SecurePass123!',
    appID: 1
  })
});

const data = await response.json();
const accessToken = data.data.accessToken;
# cURL Example
curl -X POST http://localhost:3001/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "SecurePass123!",
    "appID": 1
  }'

POST/auth/sign-up

Registers a new user account in the system.

Business Logic

  1. Validates all required fields and email format
  2. Validates password strength (minimum 8 characters with uppercase, lowercase, number, and special character)
  3. Checks if email already exists in the system
  4. Hashes password using bcrypt for secure storage
  5. Inserts new user record using parameterized queries to prevent SQL injection
  6. Creates version tracking record for audit purposes
  7. Returns encrypted user ID and user details

Request Body

{
  "email": "newuser@example.com",
  "password": "SecurePass123!",
  "firstname": "Jane",
  "lastname": "Smith",
  "appID": 1,
  "authTable": "aurora_users"
}

Parameters

  • email string required

    Email address for the new user. Must be unique in the system.

  • password string required

    Password for the account. Must be minimum 8 characters with at least one uppercase letter, one lowercase letter, one number, and one special character (@$!%*?&).

  • firstname string optional

    User's first name. Maximum 50 characters.

  • lastname string optional

    User's last name. Maximum 50 characters.

  • appID number optional

    Application ID for registration context.

  • authTable string optional

    Custom authentication table name. Defaults to appropriate table based on context.

Success Response (200 OK)

{
  "status": "Success",
  "token": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
      "id": 124,
      "email": "newuser@example.com",
      "firstName": "Jane",
      "lastName": "Smith"
    }
  }
}

Error Responses

// 409 Conflict - Email already exists
{
  "status": "Failed",
  "message": "User with this email already exists"
}

// 400 Bad Request - Password validation failed
{
  "status": "Failed",
  "message": "Password does not meet security requirements",
  "requirements": {
    "minLength": true,
    "hasUpperCase": false,
    "hasLowerCase": true,
    "hasNumber": true,
    "hasSpecialChar": false
  }
}

// 400 Bad Request - Validation error
{
  "status": "Failed",
  "message": "Validation error",
  "errors": [
    {
      "field": "password",
      "message": "Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character"
    }
  ]
}

POST/auth/refresh-token

Obtains a new access token using a valid refresh token without requiring the user to log in again.

Business Logic

  1. Validates the refresh token format and structure
  2. Decodes the refresh token using the JWT secret
  3. Extracts user ID from the token payload
  4. Retrieves current user data from aurora_users table
  5. Generates new access and refresh token pair
  6. Returns both tokens for continued authentication
  7. Access tokens expire after 1 hour; refresh tokens expire after 30 days

Request Body

{
  "payload": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Parameters

  • payload object required

    Object containing both access_token and refresh_token from previous authentication.

  • payload.access_token string required

    Current (possibly expired) access token.

  • payload.refresh_token string required

    Valid refresh token used to generate new access token.

Success Response (200 OK)

{
  "status": "Success",
  "token": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Error Responses

// 400 Bad Request - Invalid token format
{
  "status": "Failed",
  "message": "Invalid token format"
}

// 401 Unauthorized - Token expired
{
  "status": "Failed",
  "message": "Refresh token has expired"
}

// 500 Internal Server Error - Token processing failed
{
  "status": "Failed",
  "message": "An error occurred while processing your request"
}

POST/auth/sign-out

Logs out the current user by invalidating their authentication session.

Business Logic

  1. Accepts request to terminate user session
  2. Client should discard access and refresh tokens
  3. Optionally accepts refresh token for server-side invalidation
  4. Returns success confirmation
  5. Note: JWT tokens are stateless, so client-side token removal is the primary logout mechanism

Request Headers

Authorization: Bearer <access_token>
Content-Type: application/json

Request Body

{
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Parameters

  • refreshToken string optional

    Refresh token to be invalidated. Optional parameter for enhanced security.

Success Response (200 OK)

{
  "status": "Success",
  "message": "Signed out successfully"
}

Example Usage

// JavaScript Example
const response = await fetch('http://localhost:3001/auth/sign-out', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + accessToken
  },
  body: JSON.stringify({
    refreshToken: refreshToken
  })
});

// Clear tokens from local storage
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');

Notes

  • This endpoint always returns success (200 OK) as logout is primarily client-side with JWT tokens
  • No authentication is required to call this endpoint
  • The client is responsible for discarding tokens from storage
  • Server-side token blacklisting is not implemented in the current version

Password Reset Overview

Password reset is a two-step process: first requesting a reset token via email, then using that token to set a new password.

POST/auth/request-pass

Initiates a password reset by sending a secure reset token to the user's email address.

Business Logic

  1. Validates email format and required fields
  2. Retrieves application profile using appID
  3. Searches for user by email in aurora_users table
  4. Generates secure JWT reset token with user ID and expiration
  5. Constructs reset URL with domain from application profile
  6. Sends password reset email with link containing token
  7. Logs the password reset request for audit purposes
  8. Returns success message regardless of user existence (security best practice)

Request Body

{
  "email": "user@example.com",
  "appID": 1,
  "url": "https://yourdomain.com/reset-password"
}

Parameters

  • email string required

    Email address of the user requesting password reset. Must be valid email format.

  • appID number required

    Application ID to retrieve correct application context and domain.

  • url string optional

    Custom URL for the password reset page. If not provided, uses default from app profile.

Success Response (200 OK)

{
  "status": "Success",
  "message": "Email with reset password instructions was sent to user@example.com."
}

Error Responses

// 400 Bad Request - Validation error
{
  "status": "Failed",
  "message": "Validation error",
  "errors": [
    {
      "field": "email",
      "message": "Email is required"
    },
    {
      "field": "appID",
      "message": "Application ID is required"
    }
  ]
}

// 500 Internal Server Error
{
  "status": "Failed",
  "message": "An error occurred while processing your request"
}

POST/auth/reset-pass

Completes the password reset process using the token sent via email.

Business Logic

  1. Validates password strength requirements (min 8 chars, uppercase, lowercase, number, special character)
  2. Verifies password and confirmPassword match
  3. Validates reset token is not expired or tampered with
  4. Extracts user ID from verified reset token
  5. Hashes new password using bcrypt
  6. Updates user password in specified authTable using parameterized query
  7. Logs password reset completion
  8. Returns success confirmation

Request Body

{
  "password": "NewSecurePass123!",
  "confirmPassword": "NewSecurePass123!",
  "reset_password_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "authTable": "aurora_users"
}

Parameters

  • password string required

    New password. Must be minimum 8 characters with at least one uppercase letter, one lowercase letter, one number, and one special character (@$!%*?&).

  • confirmPassword string required

    Password confirmation. Must exactly match the password field.

  • reset_password_token string required

    Secure reset token received via email from /auth/request-pass endpoint.

  • authTable string optional

    Authentication table name. Defaults to 'aurora_users' if not specified.

Success Response (200 OK)

{
  "status": "Success",
  "message": "Password reset successfully"
}

Error Responses

// 400 Bad Request - Password validation failed
{
  "status": "Failed",
  "message": "Password does not meet security requirements",
  "requirements": {
    "minLength": true,
    "hasUpperCase": true,
    "hasLowerCase": true,
    "hasNumber": false,
    "hasSpecialChar": true
  }
}

// 400 Bad Request - Passwords don't match
{
  "status": "Failed",
  "message": "Validation error",
  "errors": [
    {
      "field": "confirmPassword",
      "message": "Passwords do not match"
    }
  ]
}

// 400 Bad Request - Invalid or expired token
{
  "status": "Failed",
  "message": "Reset password token has expired or is invalid"
}

// 500 Internal Server Error
{
  "status": "Failed",
  "message": "An error occurred while processing your request"
}

POST/auth/user-email

Retrieves user email and role information using an encrypted user ID token.

Business Logic

  1. Decrypts the provided encrypted userId parameter
  2. Extracts user ID and expiration date from decrypted data
  3. Validates that the token has not expired
  4. Queries aurora_users table for user_email and primary_role
  5. Returns user email and role if token is valid
  6. Used for user verification flows and access validation

Request Body

{
  "userId": "encrypted_user_id_token"
}

Parameters

  • userId string required

    Encrypted user ID token containing user ID and expiration date. Generated by the system for secure user identification.

Success Response (200 OK)

{
  "status": "Success",
  "email": "user@example.com",
  "role": "admin"
}

Error Responses

// 400 Bad Request - Token expired
{
  "status": "Failed",
  "message": "Token expired"
}

// 500 Internal Server Error - Decryption failed
{
  "status": "Failed",
  "message": "An error occurred while processing your request"
}

POST/auth/user-sms-code

Sends a one-time SMS verification code to the user's registered mobile number.

Business Logic

  1. Decrypts encrypted userId parameter to extract user ID
  2. Validates token has not expired
  3. Retrieves user's mobile number from aurora_users table
  4. Validates mobile number exists
  5. Generates secure 6-digit random verification code
  6. Hashes verification code using bcrypt and stores in database
  7. Sends SMS with verification code to user's mobile
  8. Logs SMS code generation for audit and security monitoring

Request Body

{
  "userId": "encrypted_user_id_token"
}

Parameters

  • userId string required

    Encrypted user ID token containing user ID and expiration. Same format as used in /auth/user-email.

Success Response (200 OK)

{
  "status": "Success",
  "message": "SMS code sent successfully"
}

Error Responses

// 400 Bad Request - Token expired
{
  "status": "Failed",
  "message": "Token expired"
}

// 400 Bad Request - No mobile number
{
  "status": "Failed",
  "message": "Mobile number missing"
}

// 500 Internal Server Error
{
  "status": "Failed",
  "message": "An error occurred while processing your request"
}

POST/auth/getFolderLimits

Retrieves the list of folders that a specific user has access to.

Business Logic

  1. Retrieves application user context using appID
  2. Accepts table name and user ID parameters
  3. Queries aurora_related table to find folder relationships
  4. Filters by master_id matching the provided userId
  5. Joins with aurora_folders to get folder details
  6. Returns array of folder IDs user has access to
  7. Used for access control and folder permission management

Request Body

{
  "appID": 1,
  "table": "aurora_folders",
  "userId": 123
}

Parameters

  • appID number required

    Application ID for retrieving correct application context.

  • table string required

    Table name for folder relationships (typically "aurora_folders").

  • userId number required

    User ID to retrieve folder access permissions for.

Success Response (200 OK)

{
  "status": "Success",
  "data": [
    {
      "linked": 456
    },
    {
      "linked": 789
    }
  ]
}

Error Responses

// 500 Internal Server Error
{
  "status": "Failed",
  "message": "An error occurred while processing your request"
}

OAuth / Social Login (AuthKit)

Infomaxim supports social and SSO login via an Auth Facade that keeps Infomaxim's own JWT tokens as the stable client-facing identity. The provider is WorkOS AuthKit (phase 1); the architecture is designed to swap providers without breaking existing sessions or client token shapes.

How it works

  1. Your client calls POST /auth/oauth/authkit/start to obtain an authorization URL.
  2. Redirect the user to that URL (system browser or embedded WebView). For Electron or other public clients, generate a PKCE code challenge/verifier pair first.
  3. WorkOS/AuthKit authenticates the user and redirects back to your redirectUri with a short-lived code (and the state you provided).
  4. Your client calls POST /auth/oauth/authkit/callback with the code (plus the codeVerifier for PKCE flows). Infomaxim exchanges the code with WorkOS, creates or links an aurora_identities record, and returns standard Infomaxim JWT tokens — identical in shape to POST /auth/login.
  5. Use the returned access_token and refresh_token exactly as you would for password-based login. WorkOS tokens are never exposed to the client.

Identity mapping

A new aurora_identities table links external provider users to rows in aurora_customers (or another configured authTable). Safe auto-linking by email is applied only when:

  • The provider asserts the email is verified, and
  • Exactly one matching Infomaxim user exists in the same (appID, authTable) scope with that email.

Otherwise a new user row is created. This prevents silent account takeover via email collisions.

PKCE (recommended for Electron / mobile)

// Generate a PKCE pair in the browser / renderer process
const codeVerifier  = crypto.getRandomValues(new Uint8Array(32))
                             .reduce((s,b)=>s+b.toString(16).padStart(2,'0'),'');
const encoded       = new TextEncoder().encode(codeVerifier);
const digest        = await crypto.subtle.digest('SHA-256', encoded);
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
                             .replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,'');

// Pass codeChallenge to /start, codeVerifier to /callback

Configuration

The following environment variables must be set on the API server:

  • WORKOS_API_KEY

    WorkOS secret API key (from the WorkOS Dashboard → API Keys).

  • WORKOS_CLIENT_ID

    WorkOS client ID (from the WorkOS Dashboard → Configuration).

  • WORKOS_ALLOWED_AUTH_TABLES

    Comma-separated list of authTables that may use OAuth login. Defaults to aurora_customers. Add aurora_users only if admin SSO is required.

POST/auth/oauth/authkit/start

Returns an AuthKit authorization URL that the client should redirect the user to in order to begin the OAuth login flow. Supports PKCE for public clients (Electron, mobile).

Business Logic

  1. Validates that appID and redirectUri are present.
  2. Verifies the app exists using the Infomaxim app registry.
  3. Builds an AuthKit authorization URL via the WorkOS SDK, optionally embedding a PKCE challenge.
  4. Returns the URL and the echoed state value.

Request Body

{
  "appID": 42,
  "redirectUri": "https://your-app.com/auth/callback",
  "authTable": "aurora_customers",
  "state": "random-csrf-state-string",
  "codeChallenge": "base64url-encoded-sha256-of-verifier",
  "codeChallengeMethod": "S256"
}

Parameters

  • appID number required

    Infomaxim application ID.

  • redirectUri string required

    URI that WorkOS will redirect to after authentication. Must match a URI registered in the WorkOS Dashboard.

  • authTable string optional

    Infomaxim identity table. Defaults to aurora_customers. Pass this forward to /callback.

  • state string optional

    Opaque client-generated value forwarded unchanged to the callback. Use for CSRF protection — verify it in the callback response matches what you sent here.

  • codeChallenge string optional

    PKCE code challenge (Base64URL-encoded SHA-256 of the codeVerifier). Required for public clients.

  • codeChallengeMethod string optional

    PKCE method. Only "S256" is accepted. Defaults to "S256" when codeChallenge is provided.

Success Response (200 OK)

{
  "status": "Success",
  "authorizationUrl": "https://auth.workos.com/sso/authorize?client_id=...&redirect_uri=...&...",
  "state": "random-csrf-state-string"
}

Error Responses

// 400 Bad Request - Missing required field
{
  "status": "Failed",
  "message": "redirectUri is required"
}

// 400 Bad Request - WorkOS not configured
{
  "status": "Failed",
  "message": "WorkOS clientId (WORKOS_CLIENT_ID) is not configured"
}

Example — web client

// Step 1: get the authorization URL
const { authorizationUrl, state } = await fetch('/auth/oauth/authkit/start', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    appID: 42,
    redirectUri: 'https://your-app.com/auth/callback',
    state: crypto.randomUUID()          // store this for CSRF check
  })
}).then(r => r.json());

// Step 2: redirect the user
window.location.href = authorizationUrl;

Example — Electron (PKCE)

// Generate a PKCE pair before calling /start
const codeVerifier  = crypto.getRandomValues(new Uint8Array(32))
                             .reduce((s,b)=>s+b.toString(16).padStart(2,'0'),'');
const digest        = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
                             .replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,'');

const { authorizationUrl } = await fetch('/auth/oauth/authkit/start', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    appID: 42,
    redirectUri: 'myapp://auth',        // deep-link URI registered in WorkOS
    codeChallenge,
    codeChallengeMethod: 'S256'
  })
}).then(r => r.json());

// Open the system browser
shell.openExternal(authorizationUrl);   // Electron shell API

POST/auth/oauth/authkit/callback

Exchanges the authorization code returned by WorkOS for a pair of Infomaxim JWT tokens. The response is identical in shape to POST /auth/login, so no client-side changes are needed beyond calling this endpoint instead.

Business Logic

  1. Validates that appID, code, and redirectUri are present.
  2. Verifies the app exists using the Infomaxim app registry.
  3. Exchanges the code with WorkOS (passing codeVerifier for PKCE flows) to obtain the WorkOS user object.
  4. Looks up an existing aurora_identities row for (provider=authkit, providerUserId) within the same appID and authTable.
  5. If none found and the email is verified, checks for a single matching aurora_customers row and links it.
  6. If no match, creates a new aurora_customers (or authTable) row and a new identity mapping.
  7. Issues Infomaxim JWT access + refresh tokens (same as /auth/login) for the resolved user.
  8. WorkOS tokens are discarded server-side and never returned to the client.

Request Body

{
  "appID": 42,
  "code": "01HXYZ...",
  "redirectUri": "https://your-app.com/auth/callback",
  "authTable": "aurora_customers",
  "codeVerifier": "your-pkce-code-verifier",
  "state": "random-csrf-state-string"
}

Parameters

  • appID number required

    Infomaxim application ID.

  • code string required

    Authorization code returned by WorkOS in the callback redirect (query parameter ?code=…).

  • redirectUri string required

    Must exactly match the redirectUri used in /auth/oauth/authkit/start.

  • authTable string optional

    Infomaxim identity table. Defaults to aurora_customers. Must match the value passed to /start.

  • codeVerifier string optional

    PKCE code verifier. Required when codeChallenge was supplied to /start.

  • state string optional

    Echoed back from WorkOS. Verify this matches the value you sent to /start before calling this endpoint (CSRF protection).

Success Response (200 OK)

{
  "status": "Success",
  "token": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 3600
  },
  "user": {
    "_id": "encrypted-user-id",
    "email": "user@example.com",
    "firstname": "Jane",
    "lastname": "Smith",
    "displayname": "Jane Smith",
    "photourl": "https://workos.com/profile-pic.jpg",
    "emailverified": true
  }
}

Error Responses

// 400 Bad Request - Missing required field
{
  "status": "Failed",
  "message": "code is required"
}

// 400 Bad Request - Code invalid or expired
{
  "status": "Failed",
  "message": "invalid_grant: Authorization code has already been used."
}

// 400 Bad Request - authTable not allowed
{
  "status": "Failed",
  "message": "OAuth login into authTable 'aurora_users' is not allowed by configuration."
}

// 500 Internal Server Error
{
  "status": "Failed",
  "message": "An error occurred while processing your request"
}

Example — web client (callback handler)

// On your redirectUri page, extract query params from the URL
const params      = new URLSearchParams(window.location.search);
const code        = params.get('code');
const returnedState = params.get('state');

// CSRF check: verify state matches what you stored before calling /start
if (returnedState !== sessionStorage.getItem('authkit_state')) {
  throw new Error('State mismatch – possible CSRF attack');
}

const { status, token, user } = await fetch('/auth/oauth/authkit/callback', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    appID: 42,
    code,
    redirectUri: 'https://your-app.com/auth/callback'
  })
}).then(r => r.json());

if (status === 'Success') {
  // Treat exactly the same as a /auth/login response
  localStorage.setItem('access_token',  token.access_token);
  localStorage.setItem('refresh_token', token.refresh_token);
}

Example — Electron (PKCE)

// In your deep-link handler (app.on('open-url') / custom protocol)
app.on('open-url', async (event, url) => {
  const params      = new URL(url).searchParams;
  const code        = params.get('code');

  const { token, user } = await fetch('http://localhost:3001/auth/oauth/authkit/callback', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      appID: 42,
      code,
      redirectUri: 'myapp://auth',
      codeVerifier: store.get('authkit_code_verifier')  // saved before calling /start
    })
  }).then(r => r.json());

  // Store Infomaxim tokens in the Electron secure store
  secureStore.set('access_token',  token.access_token);
  secureStore.set('refresh_token', token.refresh_token);
});