Okta outbound user lifecycle (Floh → Okta)¶
Push create, update, and deactivate from Floh into an Okta directory using the built-in okta connector and the Okta Management API.
Not the generic
scimconnector. Okta orgs do not servehttps://<org>.okta.com/scim/v2(that URL returns 404). Okta is a SCIM client when provisioning to apps; Floh inbound SCIM is documented in Okta setup for Floh.
| Topic | Document |
|---|---|
| Connector reference | Okta directory connector |
| Inbound (Okta → Floh) | Okta setup for Floh |
| Other IdPs (SCIM) | Outbound SCIM connector |
Entitlements / role_grant |
Roles & entitlements |
What Floh sends to Okta¶
| Floh command | Okta Management API | Effect |
|---|---|---|
createUser |
POST /api/v1/users |
New user (activate defaults to true) |
updateUser |
POST /api/v1/users/{id} |
Profile update |
deactivateUser |
POST /api/v1/users/{id}/lifecycle/deactivate |
Deprovision |
getUser |
GET /api/v1/users/{idOrLogin} |
Lookup |
checkUserActive |
GET /api/v1/users/{id} |
status === ACTIVE |
Implementation: packages/server/src/modules/connectors/handlers/okta-support/user-commands.ts.
1. Okta prerequisites¶
-
Developer or production org — same org as OIDC is fine; this is separate from inbound SCIM.
-
API token — Security → API → Tokens → Create token. Use a token that can create, read, update, and deactivate users (super-admin token is typical in dev).
-
Org URL (not the Admin Console host):
Example: https://integrator-5887742.okta.com — not https://integrator-5887742-admin.okta.com.
- Floh egress to
*.okta.comover HTTPS.
2. Create the Floh okta connector instance¶
UI: Connectors → New Connector → Built-in → type okta
| Field | Value |
|---|---|
| orgUrl | https://<your-org>.okta.com |
| apiToken | Okta API token (secret) |
Test: run test (GET /api/v1/users?limit=1 with SSWS auth).
3. Field mapping¶
Floh createUser param |
Okta profile |
Notes |
|---|---|---|
userName (required) |
login |
Work email |
email |
email |
Defaults to userName |
givenName / familyName |
firstName / lastName |
Default User / Account if omitted |
displayName |
displayName |
Optional |
department / title |
department / title |
Optional |
active |
?activate= query |
Default true |
sendEmail |
?sendEmail=true |
Default false |
linkExistingOnConflict |
— | Default true (idempotent create) |
4. Manual smoke test (Commands tab)¶
Create¶
{
"command": "createUser",
"userName": "scim-test@example.com",
"givenName": "SCIM",
"familyName": "Test",
"email": "scim-test@example.com",
"active": true,
"sendEmail": false
}
Confirm the user in Directory → People.
Update¶
{
"command": "updateUser",
"id": "<okta-user-id-from-create>",
"displayName": "SCIM Test Updated",
"title": "Engineer"
}
Deactivate¶
Verify¶
Expect isActive: false after deactivate.
5. Workflow example (onboarding)¶
Use a connector workflow step with your okta instance and command createUser. Map workflow variables into userName, givenName, familyName, and email. Store the returned userId in a run variable for later steps.
Offboarding: deactivateUser with id set to the Okta user id saved at provision time.
6. Entitlements caveat¶
deactivateUser requires the Okta id (00u…), not the login email. Until entitlement deprovision passes entitlement_instance.external_id into connector config, resolve the id with getUser (userName) before deactivate in workflows.
7. Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| 404 on connector test | Used scim connector or /scim/v2 base URL |
Switch to okta connector and orgUrl only |
404 with correct okta connector |
Rare — wrong org hostname | Use https://<org>.okta.com without -admin |
| 401 / 403 | Token lacks permissions | Create token with user lifecycle permissions |
| Duplicate login on create | User already exists | Enable linkExistingOnConflict (default) or use getUser first |
curl sanity check (replace org and token):
curl -sS -o /dev/null -w "%{http_code}\n" \
-H "Authorization: SSWS YOUR_TOKEN" \
-H "Accept: application/json" \
"https://integrator-5887742.okta.com/api/v1/users?limit=1"
Expect 200, not 404.