# Authentication

## Overview

To use any authenticated API, you must first acquire a valid API token.

Crisisworks uses OAuth2 time-limited access tokens, and offers an `auth` endpoint to fetch the tokens. MFA is supported using TOTP codes.

## Crisisworks account & Duty Status

{% hint style="info" %}
API users must be approved by both Datalink and the Customer of the tenancy, and agree to the [API Terms Of Use](https://docs.cw.crisisworks.com/security-and-support/security/api-terms-of-use). If you do not see the **API User** position, contact Datalink Support to organise these approvals.
{% endhint %}

API users use the same security system as regular users.

It is recommended your API-enabled user account is only used for the API, as such give it a name like **API User**.

* At minimum, this user will require the *API User* position allocated to it. If you cannot find this position, contact Datalink support.
* Ensure this user also has the levels of access required to access the data it needs to view or edit for the specific registers.
* If this API user is going to edit data in registers, **the API user MUST BE ON DUTY** in the event in one or more positions to have access to read or write data in the registers.
* If the API user is only updating assets or asset contacts, this data is not in a register, so they don't need to be on duty in any events.

## Fetching an authentication token

To fetch an authentication token, call the `auth` API command and supply the user credentials. Then supply that token to all authenticated API commands using the `Authorization` header to perform the command on behalf of that user.

Authentication tokens are OAuth2 JWT Access Tokens which expire after a short period. Therefore, for recurring use of the API you should store the username and password securely at your side and call the `auth` endpoint to fetch a new token.

The API also supports optional TOTP MFA, and you can supply `mfaCode` at the same time as supplying the `username` and `password`. The POSIX `authtool` utility can be used for this.

To call the `auth` API, use the following:

```http
POST https://{{host}}/api/json/auth
Accept: application/json
Content-Type: application/json;charset=UTF-8
X-Site: {{site}}

{
  "version": "1",
  "uuid": "{{uuid}}",
  "platform": "Windows",
  "name": "Sync asset contacts",
  "username": "{{username}}",
  "password": "{{password}}",
  "mfaCode": "{{mfaCode}}"
}
```

<table><thead><tr><th width="161.68359375">Parameter</th><th width="124.7578125">Type</th><th width="116.96875">Mandatory</th><th>Description</th></tr></thead><tbody><tr><td>uuid</td><td>string</td><td>Yes</td><td>A unique code that represents the connected device. For devices this is the device's UUID code. For servers, this should be the server's URL.</td></tr><tr><td>platform</td><td>string</td><td>Yes</td><td>A freetext string representing the type of connection — e.g. iPad, iPhone, Windows, Linux</td></tr><tr><td>version</td><td>string</td><td>Yes</td><td>The version of the client software — e.g. 11</td></tr><tr><td>name</td><td>string</td><td>Yes</td><td>The name of the device or integration — e.g. "Integration X"</td></tr><tr><td>username</td><td>string</td><td>Yes</td><td>The user username</td></tr><tr><td>password</td><td>string</td><td>Yes</td><td>The user's password</td></tr><tr><td>mfaCode</td><td>string</td><td>No</td><td>An optional TOTP MFA code</td></tr></tbody></table>

If the user is successfully authenticated, the command returns a JSON object with the following values:

```json
{
  "status": 200,
  "token": "eyJraWQiOiJCelhSZVwvdW9cL0VUczJjUUJF...",
  "type": "accessToken"
}
```

<table><thead><tr><th width="169.7421875">Key</th><th width="131.8203125">Type</th><th>Description</th></tr></thead><tbody><tr><td>token</td><td>string</td><td>An OAuth2 Access Token which can be used to perform actions on behalf of a nominated user</td></tr><tr><td>status</td><td>int</td><td>200 for success, 400 for invalid intput, 401 for invalid credentials, 500 for internal server errors</td></tr><tr><td>type</td><td>string</td><td>The type of token returned (this will always be 'accessToken')</td></tr></tbody></table>

If the user's credentials are invalid, the server responds with a **401** HTTP error.

```json
{
  "status":401,
  "message":"Invalid user, password or mfaCode"
}
```

The access token will have a 1 hour expiry, so you will need to re-authenticate for each session.

## Using `oauthtool` for CLI-based MFA

The `oauthtool` utility is open source software that can be downloaded using your package manager.

* Debian/Ubuntu: `sudo apt install oathtool`
* MacOS: `brew install oath-toolkit`
* Windows (WSL or Chocolatey): `choco install oath-toolkit`

To generate TOTP codes using `oathtool`, you need the TOTP secret key from Crisisworks. Which can be found by clicking the link "Get code for manual entry" under the QR code when registering the TOTP device.

Next, register the TOTP by entering the following command with your TOTP secret.

```
oathtool --totp -b "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
```

Each time this is run, this command will return a six-digit TOTP code. You can run it once and enter into Crisisworks to register the tool with Crisisworks for MFA.

## Using an authentication token&#x20;

Once you have acquired the auth token, you can use it for all APIs via the `Authorization` header as follows.

```http
GET https://{{host}}/api/json/item/id/1
Accept: application/json
Content-Type: application/json;charset=UTF-8
Authorization: Bearer {{token}}
X-Site: {{site}}
```

{% hint style="info" %}
Both `Authorization` and `X-Site` are required for most API calls.
{% endhint %}

## Treat your secrets securely

The examples in this guide take shortcuts for readability, with the main one being the storage of secrets in environment variables.

For production use:

* API users typically have high degrees of access.
* Treat your API user's password and TOTP secret as sensitive secrets.
* Don’t hardcode secrets in scripts — use environment variables or secure vault utilities.

For secure storage options, consider using these utilities such as these.

* [CredentialManager](https://www.powershellgallery.com/packages/CredentialManager) in PowerShell
* [Secrets Manager](https://aws.amazon.com/secrets-manager/) on AWS
* [pass](https://www.passwordstore.org/) on Linux
* Password managers (such as [1Password](https://1password.com)) also offer CLI tools to store secrets

## Examples

### Authenticating in BASH

The following simple example shows how to call the `auth` command from a bash script.

```bash
# ---- Configuration ----
HOST="api.cw.crisisworks.com"
SITE="sandbox"
UUID="example-uuid-123"
USERNAME="your.username"
PASSWORD="your.password"
CLIENT_NAME="Test Harness"

# ---- Build JSON payload ----
read -r -d '' PAYLOAD <<EOF
{
  "version": "1",
  "uuid": "$UUID",
  "platform": "curl",
  "name": "$CLIENT_NAME",
  "username": "$USERNAME",
  "password": "$PASSWORD"
}
EOF

# ---- Send authentication request and capture response ----
RESPONSE=$(curl -s -X POST "https://${HOST}/api/json/auth" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json;charset=UTF-8" \
  -H "X-Site: ${SITE}" \
  -d "$PAYLOAD")

# ---- Output or process response as needed ----
echo "$RESPONSE"

```

### Authenticating with MFA in BASH

The following shell script illustrates how to authenticate with MFA in `bash` and `curl`.

```shell
#!/bin/bash

# ---- Configuration ----
HOST="api.cw.crisisworks.com"
SITE="mysite"
UUID="example-uuid-123"
USERNAME="your.username"
PASSWORD="your.password"
TOTP_SECRET="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"  # Base32-encoded TOTP secret
CLIENT_NAME="Test Harness"

# ---- Generate current MFA TOTP code using oathtool ----
MFA_CODE=$(oathtool --totp -b "$TOTP_SECRET")

# ---- Build JSON payload ----
read -r -d '' PAYLOAD <<EOF
{
  "version": "1",
  "uuid": "$UUID",
  "platform": "curl",
  "name": "$CLIENT_NAME",
  "username": "$USERNAME",
  "password": "$PASSWORD",
  "mfaCode": "$MFA_CODE"
}
EOF

# ---- Send authentication request and capture response ----
RESPONSE=$(curl -s -X POST "https://${HOST}/api/json/auth" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json;charset=UTF-8" \
  -H "X-Site: ${SITE}" \
  -d "$PAYLOAD")

# --- Extract the token using jq ---
TOKEN=$(echo "$RESPONSE" | jq -r '.token')

# --- Check token was retrieved ---
if [ -z "$TOKEN" ] || [ "$TOKEN" == "null" ]; then
  echo "❌ Failed to retrieve token."
  exit 1
fi
```

Now you can call the authenticated commands using the token.

```shell
# --- Now make some authenticated API calls ---
RESPONSE=$(curl -s -X GET "https://${HOST}/api/json/ping" \
  -H "Accept: application/json" \
  -H "Authorization: $TOKEN" \
  -H "Content-Type: application/json;charset=UTF-8" \
  -H "X-Site: $SITE")
  
# --- Output the ping response ---
echo $RESPONSE  
```

### Authenticating with MFA in PowerShell

The following example shows how to authenticate in Windows PowerShell.

```shell
# --- Configuration ---
$host     = "api.cw.crisisworks.com"
$site     = "mysite"
$uuid     = "example-uuid-123"
$username = "your.username"
$password = "your.password"
$totpSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"  # Base32-encoded TOTP secret
$clientName = "Test Harness"

# --- Generate MFA code using oathtool ---
# Ensure oathtool is in PATH (e.g., from Chocolatey or WSL)
$mfaCode = & oathtool --totp -b $totpSecret

# --- Prepare request payload ---
$body = @{
    version  = "1"
    uuid     = $uuid
    platform = "curl"
    name     = $clientName
    username = $username
    password = $password
    mfaCode  = $mfaCode
} | ConvertTo-Json -Depth 3

# --- Send POST request to fetch token ---
$response = Invoke-RestMethod -Method Post `
    -Uri "https://$host/api/json/auth" `
    -Headers @{
        "Accept"       = "application/json"
        "Content-Type" = "application/json;charset=UTF-8"
        "X-Site"       = $site
    } `
    -Body $body

# --- Extract the token from the response ---
$token = $response.token

# --- Verify token and use it ---
if (-not $token) {
    Write-Error "❌ Failed to retrieve token."
    exit 1
}
```

Now you can call the other API commands using the token.

```shell
# --- Call the authenticated /ping endpoint ---
$pingResponse = Invoke-RestMethod -Method Get `
    -Uri "https://$host/api/json/ping" `
    -Headers @{
        "Authorization" = $token
        "Accept"        = "application/json"
        "Content-Type"  = "application/json;charset=UTF-8"
        "X-Site"        = $site
    }

# --- Output the ping response ---
$pingResponse
```

### Authenticating in PHP

```php
// Configuration
$host       = "api.cw.crisisworks.com";
$site       = "mysite";
$uuid       = "example-uuid-123";
$username   = "your.username";
$password   = "your.password";
$totpSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";  // Base32-encoded TOTP secret
$clientName = "Test Harness";

// Build the payload
$payload = json_encode([
    "version"  => "1",
    "uuid"     => $uuid,
    "platform" => "curl",
    "name"     => $clientName,
    "username" => $username,
    "password" => $password,
    "mfaCode"  => $mfaCode,
]);

// Send the request
[$statusCode, $body] = sendByCurl(
    'POST',
    $url . '/api/json/auth',
    [
        'Accept' => 'application/json',
        'Content-Type' => 'application/json;charset=UTF-8',
    ],
    $payload
);

// Handle the result
if ($statusCode >= 200 && $statusCode < 300) {
    $bodyDecoded = json_decode($body, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new Exception("Could not parse JSON during auth registration");
    }
} else {
    throw new Exception("Got invalid http status code $statusCode during auth registration");
}

if (!($bodyDecoded['token'] ?? null)) {
    throw new Exception("Did not receive an auth token during registration");
}

// Use this token for future authenticated calls
$token = $bodyDecoded['token'];
```

The above code uses a function to perform the HTTP call

```php
function sendByCurl(string $method, string $url, array $headers, string $payload = null): array
{
    $headersAsStrings = [
        'Content-Length: ' . strlen($payload),
    ];
    foreach ($headers as $key => $val) {
        $headersAsStrings[] = $key . ': ' . $val;
    }

    $ch = curl_init();

    // configuration directives
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    // set 30 second timeout on APIs
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 60000);
    curl_setopt($ch, CURLOPT_TIMEOUT, 60);
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 60000);

    // set the header, method and payload
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headersAsStrings);
    if (strtoupper((string)$method) === 'GET') {
        // do nothing
    } else {
        if (strtoupper((string)$method) === 'POST') {
            curl_setopt($ch, CURLOPT_POST, 1);
        } else {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        }
    }
    if ($payload) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    }

    curl_setopt($ch, CURLOPT_ENCODING, ''); // not gzip, deflate

    // Call the URL
    $body = curl_exec($ch);
    $statusCode = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

    // Test the response to ensure valid json
    if ($statusCode === 200) {
        json_decode($body, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            $statusCode = 400; // invalid response data
        }
    }
    curl_close($ch);

    return [$statusCode, $body];
}
```
