> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livingsecurity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Okta (Client Credentials)

> Connect Okta using client credentials authentication to import user and system log data.

export const ConnectInPlatform = ({tile, children}) => <Step title="Enter the credentials in the Living Security Platform">
    <p>
      Completed by whoever holds Living Security access — the program owner, or the system
      admin if they've been invited (delegated setup).
    </p>
    <ol>
      <li>
        Go to <strong>Settings → Integrations → Catalog</strong>, find the{' '}
        <strong>{tile}</strong> tile, click <strong>Connect</strong>.
      </li>
      <li>
        Fill in the fields below, then click <strong>Connect</strong>.
      </li>
    </ol>
    {children}
  </Step>;

export const SystemAdminBanner = ({system, recommendDelegated}) => <Note>
    <p>
      <strong>This guide is for your {system} administrator.</strong> It covers creating API
      credentials inside {system}, which requires admin access to {system} — not to the
      Living Security Platform.
    </p>
    <p>
      If you're the Living Security <strong>program owner</strong> and don't administer {system},
      send this page to whoever does. They complete Part A and hand the credentials back to you
      (or enter them directly if you've invited them into the platform).
      {recommendDelegated && <>
          {' '}Because setup produces sensitive key material, we recommend the{' '}
          <strong>delegated setup</strong> path so the secret is never sent back to you.
        </>}
    </p>
  </Note>;

<SystemAdminBanner system="Okta" recommendDelegated={true} />

## What Living Security needs

| Credential            | Description                                                    |
| --------------------- | -------------------------------------------------------------- |
| **Client ID**         | The unique identifier for your Okta API Services application   |
| **Private Key (JWK)** | The RSA private key in JSON Web Key format                     |
| **Okta Domain**       | The subdomain of your Okta organization (e.g., `dev-12345678`) |
| **Scopes**            | Space-separated list of Okta Management API scopes             |

**Scopes:** `okta.users.read` `okta.logs.read` — `okta.users.read` backs the users sync and `okta.logs.read` backs the system log sync; both are required.

**Required role:** **Super Administrator** in Okta (to create apps and assign admin roles)

### Prerequisites

* You must have **Super Administrator** access in Okta to create API Services applications and assign administrator roles.

***

## Part A — In Okta

*Your Okta administrator completes these steps.*

<Steps>
  <Step title="Create an API Services application">
    1. Log in to your [Okta Admin Console](https://login.okta.com/).
    2. In the left sidebar, navigate to **Applications** → **Applications**.
    3. Click **Create App Integration**.
    4. Select **API Services** as the sign-in method and click **Next**.

    <img src="https://mintcdn.com/livingsecurity/GTHtlUhwN7jc5LJ-/integrations/images/okta-cc/select-api-service.png?fit=max&auto=format&n=GTHtlUhwN7jc5LJ-&q=85&s=2e8853b96753e26cefacb6d696a590d1" alt="Okta application creation dialog with API Services selected" style={{maxWidth: "550px"}} width="1040" height="615" data-path="integrations/images/okta-cc/select-api-service.png" />

    5. Give your application a name (e.g., `Living Security`) and click **Save**.
    6. On the application's **General** tab, find your **Client ID** — you'll need it in Part B.
  </Step>

  <Step title="Configure public key authentication">
    1. On the **General** tab, set **Client authentication** to **Public key / Private key**.

    <img src="https://mintcdn.com/livingsecurity/GTHtlUhwN7jc5LJ-/integrations/images/okta-cc/select-public-private-key.png?fit=max&auto=format&n=GTHtlUhwN7jc5LJ-&q=85&s=11bf4d9722ea98256eae6d363e27ae96" alt="Client authentication set to Public key / Private key" style={{maxWidth: "550px"}} width="753" height="351" data-path="integrations/images/okta-cc/select-public-private-key.png" />

    2. Disable **Proof of possession (DPoP)** — it is not supported.

    <img src="https://mintcdn.com/livingsecurity/GTHtlUhwN7jc5LJ-/integrations/images/okta-cc/disable-dpop.png?fit=max&auto=format&n=GTHtlUhwN7jc5LJ-&q=85&s=5bf7fa5c638de89038320aec54e81d83" alt="DPoP toggle disabled" style={{maxWidth: "550px"}} width="736" height="511" data-path="integrations/images/okta-cc/disable-dpop.png" />

    <Note>
      The application must use **Public key / Private key** authentication. `okta.*` scopes are only supported with this authentication method.
    </Note>
  </Step>

  <Step title="Generate and register the RSA key pair">
    **Option A: Use Okta's key generator (recommended)**

    1. On the **General** tab, scroll to the **PUBLIC KEYS** section.
    2. Click **Add Key**, then select **Generate new key**.
    3. Click **Save** — Okta generates the key pair and displays the **Private Key (JWK)** once.
    4. **Copy and save the private key JSON immediately** — it will not be shown again.

    <img src="https://mintcdn.com/livingsecurity/GTHtlUhwN7jc5LJ-/integrations/images/okta-cc/create-public-key.png?fit=max&auto=format&n=GTHtlUhwN7jc5LJ-&q=85&s=8efd633f7af59dcaff707b1c21258d3c" alt="Okta key generation dialog" style={{maxWidth: "550px"}} width="741" height="471" data-path="integrations/images/okta-cc/create-public-key.png" />

    <Warning>
      The private key is sensitive credential material. Store it in a password vault or secrets manager. **Do not email it** — use delegated setup so the admin enters it directly into the platform.
    </Warning>

    **Option B: Generate your own key**

    If you prefer to generate keys yourself, use this Node.js script (requires Node.js 18+):

    ```javascript theme={null}
    const { generateKeyPairSync } = require('crypto');
    const { privateKey, publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });

    const privateJwk = privateKey.export({ format: 'jwk' });
    const publicJwk  = publicKey.export({ format: 'jwk' });

    // Assign a kid — must match between private and public JWK
    const kid = require('crypto').randomUUID();
    privateJwk.kid = kid;
    publicJwk.kid  = kid;

    console.log('=== PUBLIC JWK (upload to Okta) ===');
    console.log(JSON.stringify(publicJwk, null, 2));
    console.log('\n=== PRIVATE JWK (paste into the Living Security Platform) ===');
    console.log(JSON.stringify(privateJwk));
    ```

    Then register the **public JWK** in Okta:

    1. On the **General** tab, scroll to the **PUBLIC KEYS** section.
    2. Click **Add Key**, paste the **public JWK JSON**, and click **Save**.
  </Step>

  <Step title="Find your Okta Domain">
    Your Okta Domain is the subdomain of your Okta organization URL:

    * In the Admin Console, your browser shows a URL like `https://dev-12345678-admin.okta.com`
    * Your **Okta Domain** is the part before `.okta.com`, ignoring `-admin` (e.g., `dev-12345678`)
    * Alternatively, go to **Settings** → **Account** in the Admin Console
  </Step>

  <Step title="Grant API scopes">
    1. In the Admin Console, navigate to **Applications** → **Applications** and open your application.
    2. Click the **Okta API Scopes** tab.
    3. Grant the required scopes:
       * `okta.users.read`
       * `okta.logs.read`

    See the [Okta Management API reference](https://developer.okta.com/docs/api/oauth2/#okta-admin-management) for the full list of available scopes.
  </Step>

  <Step title="Assign an admin role">
    Granting scopes alone is not enough — Okta Management API endpoints silently return empty results if your application has no admin role. You must assign at least **Read-Only Administrator**:

    1. In the Admin Console, navigate to **Security** → **Administrators**.
    2. Click **Add administrator assignment** and select your API Services application.

    <img src="https://mintcdn.com/livingsecurity/GTHtlUhwN7jc5LJ-/integrations/images/okta-cc/admin-role-dropdown.png?fit=max&auto=format&n=GTHtlUhwN7jc5LJ-&q=85&s=a5a4c1b9146f85a119759bbadb7a72f6" alt="Administrator assignment dropdown" style={{maxWidth: "550px"}} width="1076" height="845" data-path="integrations/images/okta-cc/admin-role-dropdown.png" />

    3. Assign the **Read-Only Administrator** role.
    4. Click **Save**.

    <img src="https://mintcdn.com/livingsecurity/GTHtlUhwN7jc5LJ-/integrations/images/okta-cc/add-admin-role.png?fit=max&auto=format&n=GTHtlUhwN7jc5LJ-&q=85&s=48e4621864fb02e6a297705d5f14f65a" alt="Read-Only Administrator role selected" style={{maxWidth: "550px"}} width="1056" height="459" data-path="integrations/images/okta-cc/add-admin-role.png" />

    <Warning>
      Without an admin role, API calls succeed with `200 OK` but return empty results — there is no `403` error to indicate the problem.
    </Warning>
  </Step>
</Steps>

***

## Part B — In the Living Security Platform

*The program owner completes this step, or the system admin if using delegated setup.*

<Steps>
  <ConnectInPlatform tile="Okta (Client Credentials)">
    | Field                 | Value                                                               |
    | --------------------- | ------------------------------------------------------------------- |
    | **Client ID**         | The Client ID from Part A, Step 1                                   |
    | **Private Key (JWK)** | The full JSON object from Part A, Step 3 (single-line or formatted) |
    | **Okta Domain**       | The subdomain from Part A, Step 4 (e.g., `dev-12345678`)            |
    | **Scopes**            | Space-separated list: `okta.users.read okta.logs.read`              |
  </ConnectInPlatform>
</Steps>

You are now connected to Okta (Client Credentials).

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Empty results (200 OK but no users returned)">
    The application is missing an admin role assignment. Okta Management API endpoints return empty arrays instead of errors when the application lacks permissions.

    **Fix:** Assign at least **Read-Only Administrator** to the application in **Security → Administrators**.
  </Accordion>

  <Accordion title="401 Unauthorized">
    The credentials are invalid. Check:

    1. Client ID is correct
    2. Private key matches the public key registered in Okta
    3. Okta Domain is correct (no `https://`, no `-admin` suffix)
  </Accordion>

  <Accordion title="403 Forbidden or insufficient_scope">
    The application is missing required scopes. Verify that `okta.users.read` and `okta.logs.read` are granted in **Applications → \[Your App] → Okta API Scopes**.
  </Accordion>

  <Accordion title="Invalid DPoP proof">
    DPoP (Demonstrating Proof of Possession) is enabled but not supported. Disable DPoP in the application's **General** settings.
  </Accordion>
</AccordionGroup>

***

<Note>Portions of this documentation are adapted from [Nango](https://nango.dev/docs/), used under the [Elastic License 2.0](https://github.com/NangoHQ/nango?tab=License-1-ov-file#readme).</Note>
