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

# Okta Directory Sync

> Provision Genesis users from Okta when Okta's SCIM push cannot reach Genesis.

Okta's SCIM push sends requests from Okta's cloud into Genesis, so it needs a public
HTTPS route to Genesis. When Genesis runs on a private network with no inbound route
from the internet, use the Okta directory sync instead: Genesis reads Okta over an
outbound HTTPS connection, the same way it already reaches Okta for sign-in.

## What the sync does

* Every user assigned to the Genesis app in Okta, directly or through a group, becomes
  a Genesis user before their first sign-in. Name and email changes in Okta are copied
  over on the next run.
* A user who already signed in before the first sync, and is assigned to the app in
  Okta, keeps their existing account: the sync adopts it, matched by email, and
  manages it from then on.
* A user who is unassigned from the app, suspended or deactivated in Okta is
  deactivated in Genesis.
* Roles come from the same group-to-role mapping Genesis applies at sign-in
  (`GENESIS_GROUP_ROLE_MAPPING_FILE` or `GENESIS_GROUP_ROLE_MAPPING`), using each
  user's Okta group names. You do not change that mapping, and sign-in and the sync
  always agree. A user removed from every mapped group loses the mapped roles, as
  long as every group named in the mapping exists in Okta. Roles assigned by hand
  that are not in the mapping are left alone.
* Synced users behave exactly like SCIM-provisioned users.

The sync runs every 12 hours once enabled. An administrator can also run it at any time
with **Sync from Okta** on **Settings → Users**.

## Set up Okta

1. In the Okta Admin Console, go to **Applications → Applications → Create App
   Integration** and choose **API Services**. Name it, for example, `Genesis directory
   sync`.
2. Under **Client Credentials**, set client authentication to **Public key / Private
   key** and add a key. Either let Okta generate one and keep the private key, or
   register the public half of a key you generated (RSA, or EC P-256). Note the key ID
   if the app has more than one key.
3. You can leave **Require Demonstrating Proof of Possession (DPoP)** on; Genesis
   supports it.
4. On **Okta API Scopes**, grant `okta.users.read`, `okta.groups.read` and
   `okta.apps.read`.
5. On **Admin roles**, assign the **Read-only Administrator** role.
6. Note the service app's **Client ID**, and the ID of the Genesis app your users sign
   in with. The app ID is in that app's Admin Console URL
   (`.../admin/app/.../instance/<app id>/...`).

## Configure Genesis

On **Settings → Users**, click **Set up Okta sync** (users with the Manage Users
permission see it). Fill in the Okta org URL, the service app's client ID, the ID of the
Genesis app in Okta, and the key ID if the service app has several keys, then paste the
service app's PEM private key as it is, line breaks included. Genesis checks the URL and
the key before it saves anything, stores the key as a Genesis secret, and never shows it
again. When changing other settings, leave the private-key box blank to keep the stored
key; to replace it, paste a new one.

To configure Genesis without the UI, for example on Kubernetes, set these environment
variables instead:

| Setting | Environment variable | Value |
| - | - | - |
| `org_url` | `GENESIS__OKTA_DIRECTORY_SYNC__ORG_URL` | The Okta org base URL, such as `https://example.okta.com` |
| `client_id` | `GENESIS__OKTA_DIRECTORY_SYNC__CLIENT_ID` | The API service app's client ID |
| `private_key` | `OKTA_DIRECTORY_SYNC_PRIVATE_KEY` | The service app's PEM private key |
| `key_id` | `GENESIS__OKTA_DIRECTORY_SYNC__KEY_ID` | The key ID, only if the app has several keys |
| `app_id` | `GENESIS__OKTA_DIRECTORY_SYNC__APP_ID` | The ID of the Genesis app in Okta |
| `enabled` | `GENESIS__OKTA_DIRECTORY_SYNC__ENABLED` | `true` to run the sync on a schedule |

On Kubernetes, put the private key in a Secret and expose it as
`OKTA_DIRECTORY_SYNC_PRIVATE_KEY`; Genesis stores it as a managed secret on first start.
A key pasted on one line with literal `\n` sequences is accepted.

Genesis needs outbound HTTPS to the Okta org. Turn on the 12-hour schedule with
`okta_directory_sync.enabled` in **Settings → Advanced Settings** or the environment
variable above; a change to `enabled` or `interval_seconds` takes effect at the next
restart.

Keep `GENESIS_SCIM_REQUIRE_PROVISIONED_USERS=false`. When it is `true`, SCIM owns the
roles of provisioned users, so the sign-in mapping stops applying to them and roles can
no longer be edited by hand.

## Run the first sync

Once the connection settings are saved, **Sync from Okta** appears on **Settings → Users**
and in the setup dialog. It reports how many users were created, updated and deactivated.
The same run is available from the API:

```bash theme={null}
curl -X POST "https://<genesis-host>/api/config/scim/okta-sync/run" \
  -H "Authorization: Bearer <admin token>" \
  -H "Content-Type: application/json" \
  -d '{}'
```

`GET /api/config/scim/okta-sync` returns whether the sync is configured and enabled,
which settings are missing, and the result of the last run since the server started.

## Safety checks

* Genesis reads everything it needs from Okta before it changes anything. If any Okta
  request fails, the run changes nothing and reports the Okta error.
* If Okta returns no assigned users, or a run would deactivate more than
  `max_deactivation_percent` (default 20%) of the provisioned users and more than five
  users, the run creates and updates users but deactivates nobody, and reports why. When
  the deactivations are intended, run the sync once with
  `{"allow_mass_deactivation": true}`.
* Users who signed in but are not assigned to the Genesis app in Okta, such as a
  break-glass administrator, are never touched by the sync.

## Troubleshooting

| Symptom | Likely cause |
| - | - |
| `invalid_client` from the token request | The private key does not match the key registered on the service app, or the client ID is wrong. |
| `403 ... You do not have permission` | The service app is missing a scope from step 4 or the admin role from step 5. |
| A group listed in **unresolved groups** | A mapping key does not match any Okta group name. Mapping keys must be the group names Okta sends at sign-in. |
| `Okta rate limit reached` | Okta throttled the org's API. The next run retries; run it later by hand if needed. |
| `org_url must be the Okta org base URL` | The URL includes a path such as `/oauth2/default`. Use only `https://<org>.okta.com`. |
