feat(okta): add complete Okta identity management integration (#3685)

* feat(okta): add complete Okta identity management integration

Add 18 Okta Management API tools covering user lifecycle (list, get,
create, update, activate, deactivate, suspend, unsuspend, reset password,
delete) and group management (list, get, create, update, delete, add/remove
members, list members). Includes block with conditional UI, icon, registry
entries, and generated docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs(okta): add manual description section to generated docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(okta): address PR review — SSRF prevention, safe response parsing, consistent sendEmail

- Add validateOktaDomain() to prevent SSRF via user-supplied domain param
- Fix 9 tools to check response.ok before calling response.json()
- Make sendEmail query param explicit in deactivate_user and delete_user

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(okta): only forward boolean switches when explicitly true

Switch subBlocks default to OFF (false), which was being forwarded to
tools and overriding their default-true behavior for sendEmail and
activate params. Now only forward these when explicitly toggled ON.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(okta): use nullish coalescing for boolean switch defaults

Block now forwards sendEmail/activate values as-is (including false).
Tools use ?? operator so: explicit true/false from switches are respected,
undefined (programmatic calls) still defaults to true.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(okta): prevent silent data loss in update operations

- update_group: always include description in PUT body (defaults to '')
  since PUT replaces the full profile object
- update_user: use !== undefined checks so empty strings can clear fields
  via Okta's POST partial update
- block: allow empty strings through passthrough loop and use !== undefined
  for groupDescription mapping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(okta): move validateOktaDomain to centralized input-validation

- Moved validateOktaDomain from tools/okta/types.ts to
  lib/core/security/input-validation.ts alongside other validation utils
- Added .trim() to handle copy-paste whitespace in domain input
- Updated all 18 tool files to import from the new location

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waleed
2026-03-19 16:01:27 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent d3daab743f
commit 6326353f5c
31 changed files with 3775 additions and 2 deletions
+13
View File
@@ -6106,6 +6106,19 @@ export function AgentSkillsIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function OktaIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 63 63' xmlns='http://www.w3.org/2000/svg'>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M34.6.4l-1.3 16c-.6-.1-1.2-.1-1.9-.1-.8 0-1.6.1-2.3.2l-.7-7.7c0-.2.2-.5.4-.5h1.3L29.5.5c0-.2.2-.5.4-.5h4.3c.3 0 .5.2.4.4zm-10.8.8c-.1-.2-.3-.4-.5-.3l-4 1.5c-.3.1-.4.4-.3.6l3.3 7.1-1.2.5c-.2.1-.3.3-.2.6l3.3 7c1.2-.7 2.5-1.2 3.9-1.5L23.8 1.2zM14 5.7l9.3 13.1c-1.2.8-2.2 1.7-3.1 2.7L14.5 16c-.2-.2-.2-.5 0-.6l1-.8L10 9c-.2-.2-.2-.5 0-.6l3.3-2.7c.2-.3.5-.2.7 0zM6.2 13.2c-.2-.1-.5-.1-.6.1l-2.1 3.7c-.1.2 0 .5.2.6l7.1 3.4-.7 1.1c-.1.2 0 .5.2.6l7.1 3.2c.5-1.3 1.2-2.5 2-3.6L6.2 13.2zM.9 23.3c0-.2.3-.4.5-.3l15.5 4c-.4 1.3-.6 2.7-.7 4.1l-7.8-.6c-.2 0-.4-.2-.4-.5l.2-1.3L.6 28c-.2 0-.4-.2-.4-.5l.7-4.2zM.4 33.8c-.3 0-.4.2-.4.5l.8 4.2c0 .2.3.4.5.3l7.6-2 .2 1.3c0 .2.3.4.5.3l7.5-2.1c-.4-1.3-.7-2.7-.8-4.1L.4 33.8zm2.5 11.1c-.1-.2 0-.5.2-.6l14.5-6.9c.5 1.3 1.3 2.5 2.2 3.6l-6.3 4.5c-.2.1-.5.1-.6-.1L12 44.3l-6.5 4.5c-.2.1-.5.1-.6-.1l-2-3.8zm17.5-3L9.1 53.3c-.2.2-.2.5 0 .6l3.3 2.7c.2.2.5.1.6-.1l4.6-6.4 1 .9c.2.2.5.1.6-.1l4.4-6.4c-1.2-.7-2.3-1.6-3.2-2.6zm-2.2 18.2c-.2-.1-.3-.3-.2-.6L24.6 45c1.2.6 2.6 1.1 3.9 1.4l-2 7.5c-.1.2-.3.4-.5.3l-1.2-.5-2.1 7.6c-.1.2-.3.4-.5.3l-4-1.5zm10.9-13.5l-1.3 16c0 .2.2.5.4.5H33c.2 0 .4-.2.4-.5l-.6-7.8h1.3c.2 0 .4-.2.4-.5l-.7-7.7c-.8.1-1.5.2-2.3.2-.6 0-1.3 0-1.9-.1zm16-43.2c.1-.2 0-.5-.2-.6l-4-1.5c-.2-.1-.5.1-.5.3l-2.1 7.6-1.2-.5c-.2-.1-.5.1-.5.3l-2 7.5c1.4.3 2.7.8 3.9 1.4l6.6-14.5zm8.8 6.3L42.6 21.1c-.9-1-2-1.9-3.2-2.6l4.4-6.4c.1-.2.4-.2.6-.1l1 .9 4.6-6.4c.1-.2.4-.2.6-.1l3.3 2.7c.2.2.2.5 0 .6zM59.9 18.7c.2-.1.3-.4.2-.6L58 14.4c-.1-.2-.4-.3-.6-.1l-6.5 4.5-.7-1.1c-.1-.2-.4-.3-.6-.1L43.3 22c.9 1.1 1.6 2.3 2.2 3.6l14.4-6.9zm2.3 5.8l.7 4.2c0 .2-.1.5-.4.5l-15.9 1.5c-.1-1.4-.4-2.8-.8-4.1l7.5-2.1c.2-.1.5.1.5.3l.2 1.3 7.6-2c.3-.1.5.1.6.4zM61.5 40c.2.1.5-.1.5-.3l.7-4.2c0-.2-.1-.5-.4-.5l-7.8-.7.2-1.3c0-.2-.1-.5-.4-.5l-7.8-.6c0 1.4-.3 2.8-.7 4.1L61.5 40zm-4.1 9.6c-.1.2-.4.3-.6.1l-13.2-9.1c.8-1.1 1.5-2.3 2-3.6l7.1 3.2c.2.1.3.4.2.6L52.2 42l7.1 3.4c.2.1.3.4.2.6l-2.1 3.6zm-17.7-5.4L49 57.3c.1.2.4.2.6.1l3.3-2.7c.2-.2.2-.4 0-.6l-5.5-5.6 1-.8c.2-.2.2-.4 0-.6l-5.5-5.5c1.1.8 0 1.7-1.2 2.4zm0 17.8c-.2.1-.5-.1-.5-.3l-4.2-15.4c1.4-.3 2.7-.8 3.9-1.5l3.3 7c.1.2 0 .5-.2.6l-1.2.5 3.3 7.1c.1.2 0 .5-.2.6L39.7 62z'
fill='currentColor'
/>
</svg>
)
}
export function OnePasswordIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg' fill='none'>
+2
View File
@@ -111,6 +111,7 @@ import {
Neo4jIcon,
NotionIcon,
ObsidianIcon,
OktaIcon,
OnePasswordIcon,
OpenAIIcon,
OutlookIcon,
@@ -282,6 +283,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
neo4j: Neo4jIcon,
notion_v2: NotionIcon,
obsidian: ObsidianIcon,
okta: OktaIcon,
onedrive: MicrosoftOneDriveIcon,
onepassword: OnePasswordIcon,
openai: OpenAIIcon,
@@ -106,6 +106,7 @@
"neo4j",
"notion",
"obsidian",
"okta",
"onedrive",
"onepassword",
"openai",
+516
View File
@@ -0,0 +1,516 @@
---
title: Okta
description: Manage users and groups in Okta
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="okta"
color="#191919"
/>
{/* MANUAL-CONTENT-START:intro */}
[Okta](https://www.okta.com/) is an identity and access management platform that provides secure authentication, authorization, and user management for organizations.
With the Okta integration in Sim, you can:
- **List and search users**: Retrieve users from your Okta org with SCIM search expressions and filters
- **Manage user lifecycle**: Create, activate, deactivate, suspend, unsuspend, and delete users
- **Update user profiles**: Modify user attributes like name, email, phone, title, and department
- **Reset passwords**: Trigger password reset flows with optional email notification
- **Manage groups**: Create, update, delete, and list groups in your organization
- **Manage group membership**: Add or remove users from groups, and list group members
In Sim, the Okta integration enables your agents to automate identity management tasks as part of their workflows. This allows for scenarios such as onboarding new employees, offboarding departing users, managing group-based access, auditing user status, and responding to security events by suspending or deactivating accounts.
## Need Help?
If you encounter issues with the Okta integration, contact us at [help@sim.ai](mailto:help@sim.ai)
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate Okta identity management into your workflow. List, create, update, activate, suspend, and delete users. Reset passwords. Manage groups and group membership.
## Tools
### `okta_list_users`
List all users in your Okta organization with optional search and filtering
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `search` | string | No | Okta search expression \(e.g., profile.firstName eq "John" or profile.email co "example.com"\) |
| `filter` | string | No | Okta filter expression \(e.g., status eq "ACTIVE"\) |
| `limit` | number | No | Maximum number of users to return \(default: 200, max: 200\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `users` | array | Array of Okta user objects |
| ↳ `id` | string | User ID |
| ↳ `status` | string | User status \(ACTIVE, STAGED, PROVISIONED, etc.\) |
| ↳ `firstName` | string | First name |
| ↳ `lastName` | string | Last name |
| ↳ `email` | string | Email address |
| ↳ `login` | string | Login \(usually email\) |
| ↳ `mobilePhone` | string | Mobile phone |
| ↳ `title` | string | Job title |
| ↳ `department` | string | Department |
| ↳ `created` | string | Creation timestamp |
| ↳ `lastLogin` | string | Last login timestamp |
| ↳ `lastUpdated` | string | Last update timestamp |
| ↳ `activated` | string | Activation timestamp |
| ↳ `statusChanged` | string | Status change timestamp |
| `count` | number | Number of users returned |
| `success` | boolean | Operation success status |
### `okta_get_user`
Get a specific user by ID or login from your Okta organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `userId` | string | Yes | User ID or login \(email\) to look up |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | User ID |
| `status` | string | User status |
| `firstName` | string | First name |
| `lastName` | string | Last name |
| `email` | string | Email address |
| `login` | string | Login \(usually email\) |
| `mobilePhone` | string | Mobile phone |
| `secondEmail` | string | Secondary email |
| `displayName` | string | Display name |
| `title` | string | Job title |
| `department` | string | Department |
| `organization` | string | Organization |
| `manager` | string | Manager name |
| `managerId` | string | Manager ID |
| `division` | string | Division |
| `employeeNumber` | string | Employee number |
| `userType` | string | User type |
| `created` | string | Creation timestamp |
| `activated` | string | Activation timestamp |
| `lastLogin` | string | Last login timestamp |
| `lastUpdated` | string | Last update timestamp |
| `statusChanged` | string | Status change timestamp |
| `passwordChanged` | string | Password change timestamp |
| `success` | boolean | Operation success status |
### `okta_create_user`
Create a new user in your Okta organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `firstName` | string | Yes | First name of the user |
| `lastName` | string | Yes | Last name of the user |
| `email` | string | Yes | Email address of the user |
| `login` | string | No | Login for the user \(defaults to email if not provided\) |
| `password` | string | No | Password for the user \(if not set, user will be emailed to set password\) |
| `mobilePhone` | string | No | Mobile phone number |
| `title` | string | No | Job title |
| `department` | string | No | Department |
| `activate` | boolean | No | Whether to activate the user immediately \(default: true\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Created user ID |
| `status` | string | User status |
| `firstName` | string | First name |
| `lastName` | string | Last name |
| `email` | string | Email address |
| `login` | string | Login |
| `created` | string | Creation timestamp |
| `lastUpdated` | string | Last update timestamp |
| `success` | boolean | Operation success status |
### `okta_update_user`
Update a user profile in your Okta organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `userId` | string | Yes | User ID or login to update |
| `firstName` | string | No | Updated first name |
| `lastName` | string | No | Updated last name |
| `email` | string | No | Updated email address |
| `login` | string | No | Updated login |
| `mobilePhone` | string | No | Updated mobile phone number |
| `title` | string | No | Updated job title |
| `department` | string | No | Updated department |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | User ID |
| `status` | string | User status |
| `firstName` | string | First name |
| `lastName` | string | Last name |
| `email` | string | Email address |
| `login` | string | Login |
| `created` | string | Creation timestamp |
| `lastUpdated` | string | Last update timestamp |
| `success` | boolean | Operation success status |
### `okta_activate_user`
Activate a user in your Okta organization. Can only be performed on users with STAGED or DEPROVISIONED status. Optionally sends an activation email.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `userId` | string | Yes | User ID or login to activate |
| `sendEmail` | boolean | No | Send activation email to the user \(default: true\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `userId` | string | Activated user ID |
| `activated` | boolean | Whether the user was activated |
| `activationUrl` | string | Activation URL \(only returned when sendEmail is false\) |
| `activationToken` | string | Activation token \(only returned when sendEmail is false\) |
| `success` | boolean | Operation success status |
### `okta_deactivate_user`
Deactivate a user in your Okta organization. This transitions the user to DEPROVISIONED status.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `userId` | string | Yes | User ID or login to deactivate |
| `sendEmail` | boolean | No | Send deactivation email to admin \(default: false\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `userId` | string | Deactivated user ID |
| `deactivated` | boolean | Whether the user was deactivated |
| `success` | boolean | Operation success status |
### `okta_suspend_user`
Suspend a user in your Okta organization. Only users with ACTIVE status can be suspended. Suspended users cannot log in but retain group and app assignments.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `userId` | string | Yes | User ID or login to suspend |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `userId` | string | Suspended user ID |
| `suspended` | boolean | Whether the user was suspended |
| `success` | boolean | Operation success status |
### `okta_unsuspend_user`
Unsuspend a previously suspended user in your Okta organization. Returns the user to ACTIVE status.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `userId` | string | Yes | User ID or login to unsuspend |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `userId` | string | Unsuspended user ID |
| `unsuspended` | boolean | Whether the user was unsuspended |
| `success` | boolean | Operation success status |
### `okta_reset_password`
Generate a one-time token to reset a user password. Can email the reset link to the user or return it directly. Transitions the user to RECOVERY status.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `userId` | string | Yes | User ID or login to reset password for |
| `sendEmail` | boolean | No | Send password reset email to the user \(default: true\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `userId` | string | User ID |
| `resetPasswordUrl` | string | Password reset URL \(only returned when sendEmail is false\) |
| `success` | boolean | Operation success status |
### `okta_delete_user`
Permanently delete a user from your Okta organization. Can only be performed on DEPROVISIONED users. If the user is active, this will first deactivate them and a second call is needed to delete.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `userId` | string | Yes | User ID to delete |
| `sendEmail` | boolean | No | Send deactivation email to admin \(default: false\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `userId` | string | Deleted user ID |
| `deleted` | boolean | Whether the user was deleted |
| `success` | boolean | Operation success status |
### `okta_list_groups`
List all groups in your Okta organization with optional search and filtering
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `search` | string | No | Okta search expression for groups \(e.g., profile.name sw "Engineering" or type eq "OKTA_GROUP"\) |
| `filter` | string | No | Okta filter expression \(e.g., type eq "OKTA_GROUP"\) |
| `limit` | number | No | Maximum number of groups to return \(default: 10000, max: 10000\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `groups` | array | Array of Okta group objects |
| ↳ `id` | string | Group ID |
| ↳ `name` | string | Group name |
| ↳ `description` | string | Group description |
| ↳ `type` | string | Group type \(OKTA_GROUP, APP_GROUP, BUILT_IN\) |
| ↳ `created` | string | Creation timestamp |
| ↳ `lastUpdated` | string | Last update timestamp |
| ↳ `lastMembershipUpdated` | string | Last membership change timestamp |
| `count` | number | Number of groups returned |
| `success` | boolean | Operation success status |
### `okta_get_group`
Get a specific group by ID from your Okta organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `groupId` | string | Yes | Group ID to look up |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Group ID |
| `name` | string | Group name |
| `description` | string | Group description |
| `type` | string | Group type |
| `created` | string | Creation timestamp |
| `lastUpdated` | string | Last update timestamp |
| `lastMembershipUpdated` | string | Last membership change timestamp |
| `success` | boolean | Operation success status |
### `okta_create_group`
Create a new group in your Okta organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `name` | string | Yes | Name of the group |
| `description` | string | No | Description of the group |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Created group ID |
| `name` | string | Group name |
| `description` | string | Group description |
| `type` | string | Group type |
| `created` | string | Creation timestamp |
| `lastUpdated` | string | Last update timestamp |
| `lastMembershipUpdated` | string | Last membership change timestamp |
| `success` | boolean | Operation success status |
### `okta_update_group`
Update a group profile in your Okta organization. Only groups of OKTA_GROUP type can be updated. All profile properties must be specified (full replacement).
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `groupId` | string | Yes | Group ID to update |
| `name` | string | Yes | Updated group name |
| `description` | string | No | Updated group description |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Group ID |
| `name` | string | Group name |
| `description` | string | Group description |
| `type` | string | Group type |
| `created` | string | Creation timestamp |
| `lastUpdated` | string | Last update timestamp |
| `lastMembershipUpdated` | string | Last membership change timestamp |
| `success` | boolean | Operation success status |
### `okta_delete_group`
Delete a group from your Okta organization. Groups of OKTA_GROUP or APP_GROUP type can be removed.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `groupId` | string | Yes | Group ID to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `groupId` | string | Deleted group ID |
| `deleted` | boolean | Whether the group was deleted |
| `success` | boolean | Operation success status |
### `okta_add_user_to_group`
Add a user to a group in your Okta organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `groupId` | string | Yes | Group ID to add the user to |
| `userId` | string | Yes | User ID to add to the group |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `groupId` | string | Group ID |
| `userId` | string | User ID added to the group |
| `added` | boolean | Whether the user was added |
| `success` | boolean | Operation success status |
### `okta_remove_user_from_group`
Remove a user from a group in your Okta organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `groupId` | string | Yes | Group ID to remove the user from |
| `userId` | string | Yes | User ID to remove from the group |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `groupId` | string | Group ID |
| `userId` | string | User ID removed from the group |
| `removed` | boolean | Whether the user was removed |
| `success` | boolean | Operation success status |
### `okta_list_group_members`
List all members of a specific group in your Okta organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Okta API token for authentication |
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
| `groupId` | string | Yes | Group ID to list members for |
| `limit` | number | No | Maximum number of members to return \(default: 1000, max: 1000\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `members` | array | Array of group member user objects |
| ↳ `id` | string | User ID |
| ↳ `status` | string | User status |
| ↳ `firstName` | string | First name |
| ↳ `lastName` | string | Last name |
| ↳ `email` | string | Email address |
| ↳ `login` | string | Login |
| ↳ `mobilePhone` | string | Mobile phone |
| ↳ `title` | string | Job title |
| ↳ `department` | string | Department |
| ↳ `created` | string | Creation timestamp |
| ↳ `lastLogin` | string | Last login timestamp |
| ↳ `lastUpdated` | string | Last update timestamp |
| ↳ `activated` | string | Activation timestamp |
| ↳ `statusChanged` | string | Status change timestamp |
| `count` | number | Number of members returned |
| `success` | boolean | Operation success status |
@@ -16,6 +16,7 @@ import {
AsanaIcon,
AshbyIcon,
AttioIcon,
BoxCompanyIcon,
BrainIcon,
BrandfetchIcon,
BrowserUseIcon,
@@ -32,6 +33,7 @@ import {
DevinIcon,
DiscordIcon,
DocumentIcon,
DocuSignIcon,
DropboxIcon,
DsPyIcon,
DubIcon,
@@ -107,6 +109,7 @@ import {
Neo4jIcon,
NotionIcon,
ObsidianIcon,
OktaIcon,
OnePasswordIcon,
OpenAIIcon,
OutlookIcon,
@@ -162,6 +165,7 @@ import {
WhatsAppIcon,
WikipediaIcon,
WordpressIcon,
WorkdayIcon,
xIcon,
YouTubeIcon,
ZendeskIcon,
@@ -184,6 +188,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
asana: AsanaIcon,
ashby: AshbyIcon,
attio: AttioIcon,
box: BoxCompanyIcon,
brandfetch: BrandfetchIcon,
browser_use: BrowserUseIcon,
calcom: CalComIcon,
@@ -198,6 +203,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
datadog: DatadogIcon,
devin: DevinIcon,
discord: DiscordIcon,
docusign: DocuSignIcon,
dropbox: DropboxIcon,
dspy: DsPyIcon,
dub: DubIcon,
@@ -273,6 +279,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
neo4j: Neo4jIcon,
notion_v2: NotionIcon,
obsidian: ObsidianIcon,
okta: OktaIcon,
onedrive: MicrosoftOneDriveIcon,
onepassword: OnePasswordIcon,
openai: OpenAIIcon,
@@ -331,6 +338,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
whatsapp: WhatsAppIcon,
wikipedia: WikipediaIcon,
wordpress: WordpressIcon,
workday: WorkdayIcon,
x: xIcon,
youtube: YouTubeIcon,
zendesk: ZendeskIcon,
@@ -683,7 +683,7 @@
"slug": "ashby",
"name": "Ashby",
"description": "Manage candidates, jobs, and applications in Ashby",
"longDescription": "Integrate Ashby into the workflow. Can list, search, create, and update candidates, list and get job details, create notes, list notes, list and get applications, create applications, and list offers.",
"longDescription": "Integrate Ashby into the workflow. Manage candidates (list, get, create, update, search, tag), applications (list, get, create, change stage), jobs (list, get), job postings (list, get), offers (list, get), notes (list, create), interviews (list), and reference data (sources, tags, archive reasons, custom fields, departments, locations, openings, users).",
"bgColor": "#5D4ED6",
"iconName": "AshbyIcon",
"docsUrl": "https://docs.sim.ai/tools/ashby",
@@ -739,9 +739,69 @@
{
"name": "List Offers",
"description": "Lists all offers with their latest version in an Ashby organization."
},
{
"name": "Change Application Stage",
"description": "Moves an application to a different interview stage. Requires an archive reason when moving to an Archived stage."
},
{
"name": "Add Candidate Tag",
"description": "Adds a tag to a candidate in Ashby."
},
{
"name": "Remove Candidate Tag",
"description": "Removes a tag from a candidate in Ashby."
},
{
"name": "Get Offer",
"description": "Retrieves full details about a single offer by its ID."
},
{
"name": "List Sources",
"description": "Lists all candidate sources configured in Ashby."
},
{
"name": "List Candidate Tags",
"description": "Lists all candidate tags configured in Ashby."
},
{
"name": "List Archive Reasons",
"description": "Lists all archive reasons configured in Ashby."
},
{
"name": "List Custom Fields",
"description": "Lists all custom field definitions configured in Ashby."
},
{
"name": "List Departments",
"description": "Lists all departments in Ashby."
},
{
"name": "List Locations",
"description": "Lists all locations configured in Ashby."
},
{
"name": "List Job Postings",
"description": "Lists all job postings in Ashby."
},
{
"name": "Get Job Posting",
"description": "Retrieves full details about a single job posting by its ID."
},
{
"name": "List Openings",
"description": "Lists all openings in Ashby with pagination."
},
{
"name": "List Users",
"description": "Lists all users in Ashby with pagination."
},
{
"name": "List Interviews",
"description": "Lists interview schedules in Ashby, optionally filtered by application or interview stage."
}
],
"operationCount": 13,
"operationCount": 28,
"triggers": [
{
"id": "ashby_application_submit",
@@ -1062,6 +1122,83 @@
"authType": "none",
"category": "tools"
},
{
"type": "box",
"slug": "box",
"name": "Box",
"description": "Manage files, folders, and e-signatures with Box",
"longDescription": "Integrate Box into your workflow to manage files, folders, and e-signatures. Upload and download files, search content, create folders, send documents for e-signature, track signing status, and more.",
"bgColor": "#FFFFFF",
"iconName": "BoxCompanyIcon",
"docsUrl": "https://docs.sim.ai/tools/box",
"operations": [
{
"name": "Upload File",
"description": "Upload a file to a Box folder"
},
{
"name": "Download File",
"description": "Download a file from Box"
},
{
"name": "Get File Info",
"description": "Get detailed information about a file in Box"
},
{
"name": "List Folder Items",
"description": "List files and folders in a Box folder"
},
{
"name": "Create Folder",
"description": "Create a new folder in Box"
},
{
"name": "Delete File",
"description": "Delete a file from Box"
},
{
"name": "Delete Folder",
"description": "Delete a folder from Box"
},
{
"name": "Copy File",
"description": "Copy a file to another folder in Box"
},
{
"name": "Search",
"description": "Search for files and folders in Box"
},
{
"name": "Update File",
"description": "Update file info in Box (rename, move, change description, add tags)"
},
{
"name": "Create Sign Request",
"description": "Create a new Box Sign request to send documents for e-signature"
},
{
"name": "Get Sign Request",
"description": "Get the details and status of a Box Sign request"
},
{
"name": "List Sign Requests",
"description": "List all Box Sign requests"
},
{
"name": "Cancel Sign Request",
"description": "Cancel a pending Box Sign request"
},
{
"name": "Resend Sign Request",
"description": "Resend a Box Sign request to signers who have not yet signed"
}
],
"operationCount": 15,
"triggers": [],
"triggerCount": 0,
"authType": "oauth",
"category": "tools"
},
{
"type": "brandfetch",
"slug": "brandfetch",
@@ -2117,6 +2254,55 @@
"authType": "none",
"category": "tools"
},
{
"type": "docusign",
"slug": "docusign",
"name": "DocuSign",
"description": "Send documents for e-signature via DocuSign",
"longDescription": "Create and send envelopes for e-signature, use templates, check signing status, download signed documents, and manage recipients with DocuSign.",
"bgColor": "#FFFFFF",
"iconName": "DocuSignIcon",
"docsUrl": "https://docs.sim.ai/tools/docusign",
"operations": [
{
"name": "Send Envelope",
"description": "Create and send a DocuSign envelope with a document for e-signature"
},
{
"name": "Send from Template",
"description": "Create and send a DocuSign envelope using a pre-built template"
},
{
"name": "Get Envelope",
"description": "Get the details and status of a DocuSign envelope"
},
{
"name": "List Envelopes",
"description": "List envelopes from your DocuSign account with optional filters"
},
{
"name": "Void Envelope",
"description": "Void (cancel) a sent DocuSign envelope that has not yet been completed"
},
{
"name": "Download Document",
"description": "Download a signed document from a completed DocuSign envelope"
},
{
"name": "List Templates",
"description": "List available templates in your DocuSign account"
},
{
"name": "List Recipients",
"description": "Get the recipient status details for a DocuSign envelope"
}
],
"operationCount": 8,
"triggers": [],
"triggerCount": 0,
"authType": "oauth",
"category": "tools"
},
{
"type": "dropbox",
"slug": "dropbox",
@@ -7208,6 +7394,95 @@
"authType": "api-key",
"category": "tools"
},
{
"type": "okta",
"slug": "okta",
"name": "Okta",
"description": "Manage users and groups in Okta",
"longDescription": "Integrate Okta identity management into your workflow. List, create, update, activate, suspend, and delete users. Reset passwords. Manage groups and group membership.",
"bgColor": "#191919",
"iconName": "OktaIcon",
"docsUrl": "https://docs.sim.ai/tools/okta",
"operations": [
{
"name": "List Users",
"description": "List all users in your Okta organization with optional search and filtering"
},
{
"name": "Get User",
"description": "Get a specific user by ID or login from your Okta organization"
},
{
"name": "Create User",
"description": "Create a new user in your Okta organization"
},
{
"name": "Update User",
"description": "Update a user profile in your Okta organization"
},
{
"name": "Activate User",
"description": "Activate a user in your Okta organization. Can only be performed on users with STAGED or DEPROVISIONED status. Optionally sends an activation email."
},
{
"name": "Deactivate User",
"description": "Deactivate a user in your Okta organization. This transitions the user to DEPROVISIONED status."
},
{
"name": "Suspend User",
"description": "Suspend a user in your Okta organization. Only users with ACTIVE status can be suspended. Suspended users cannot log in but retain group and app assignments."
},
{
"name": "Unsuspend User",
"description": "Unsuspend a previously suspended user in your Okta organization. Returns the user to ACTIVE status."
},
{
"name": "Reset Password",
"description": "Generate a one-time token to reset a user password. Can email the reset link to the user or return it directly. Transitions the user to RECOVERY status."
},
{
"name": "Delete User",
"description": "Permanently delete a user from your Okta organization. Can only be performed on DEPROVISIONED users. If the user is active, this will first deactivate them and a second call is needed to delete."
},
{
"name": "List Groups",
"description": "List all groups in your Okta organization with optional search and filtering"
},
{
"name": "Get Group",
"description": "Get a specific group by ID from your Okta organization"
},
{
"name": "Create Group",
"description": "Create a new group in your Okta organization"
},
{
"name": "Update Group",
"description": "Update a group profile in your Okta organization. Only groups of OKTA_GROUP type can be updated. All profile properties must be specified (full replacement)."
},
{
"name": "Delete Group",
"description": "Delete a group from your Okta organization. Groups of OKTA_GROUP or APP_GROUP type can be removed."
},
{
"name": "Add User to Group",
"description": "Add a user to a group in your Okta organization"
},
{
"name": "Remove User from Group",
"description": "Remove a user from a group in your Okta organization"
},
{
"name": "List Group Members",
"description": "List all members of a specific group in your Okta organization"
}
],
"operationCount": 18,
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
"category": "tools"
},
{
"type": "onedrive",
"slug": "onedrive",
@@ -10302,6 +10577,63 @@
"authType": "oauth",
"category": "tools"
},
{
"type": "workday",
"slug": "workday",
"name": "Workday",
"description": "Manage workers, hiring, onboarding, and HR operations in Workday",
"longDescription": "Integrate Workday HRIS into your workflow. Create pre-hires, hire employees, manage worker profiles, assign onboarding plans, handle job changes, retrieve compensation data, and process terminations.",
"bgColor": "#F5F0EB",
"iconName": "WorkdayIcon",
"docsUrl": "https://docs.sim.ai/tools/workday",
"operations": [
{
"name": "Get Worker",
"description": "Retrieve a specific worker profile including personal, employment, and organization data."
},
{
"name": "List Workers",
"description": "List or search workers with optional filtering and pagination."
},
{
"name": "Create Pre-Hire",
"description": "Create a new pre-hire (applicant) record in Workday. This is typically the first step before hiring an employee."
},
{
"name": "Hire Employee",
"description": "Hire a pre-hire into an employee position. Converts an applicant into an active employee record with position, start date, and manager assignment."
},
{
"name": "Update Worker",
"description": "Update fields on an existing worker record in Workday."
},
{
"name": "Assign Onboarding Plan",
"description": "Create or update an onboarding plan assignment for a worker. Sets up onboarding stages and manages the assignment lifecycle."
},
{
"name": "Get Organizations",
"description": "Retrieve organizations, departments, and cost centers from Workday."
},
{
"name": "Change Job",
"description": "Perform a job change for a worker including transfers, promotions, demotions, and lateral moves."
},
{
"name": "Get Compensation",
"description": "Retrieve compensation plan details for a specific worker."
},
{
"name": "Terminate Worker",
"description": "Initiate a worker termination in Workday. Triggers the Terminate Employee business process."
}
],
"operationCount": 10,
"triggers": [],
"triggerCount": 0,
"authType": "none",
"category": "tools"
},
{
"type": "x",
"slug": "x",
+381
View File
@@ -0,0 +1,381 @@
import { OktaIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { OktaResponse } from '@/tools/okta/types'
export const OktaBlock: BlockConfig<OktaResponse> = {
type: 'okta',
name: 'Okta',
description: 'Manage users and groups in Okta',
longDescription:
'Integrate Okta identity management into your workflow. List, create, update, activate, suspend, and delete users. Reset passwords. Manage groups and group membership.',
docsLink: 'https://docs.sim.ai/tools/okta',
category: 'tools',
bgColor: '#191919',
icon: OktaIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'List Users', id: 'okta_list_users' },
{ label: 'Get User', id: 'okta_get_user' },
{ label: 'Create User', id: 'okta_create_user' },
{ label: 'Update User', id: 'okta_update_user' },
{ label: 'Activate User', id: 'okta_activate_user' },
{ label: 'Deactivate User', id: 'okta_deactivate_user' },
{ label: 'Suspend User', id: 'okta_suspend_user' },
{ label: 'Unsuspend User', id: 'okta_unsuspend_user' },
{ label: 'Reset Password', id: 'okta_reset_password' },
{ label: 'Delete User', id: 'okta_delete_user' },
{ label: 'List Groups', id: 'okta_list_groups' },
{ label: 'Get Group', id: 'okta_get_group' },
{ label: 'Create Group', id: 'okta_create_group' },
{ label: 'Update Group', id: 'okta_update_group' },
{ label: 'Delete Group', id: 'okta_delete_group' },
{ label: 'Add User to Group', id: 'okta_add_user_to_group' },
{ label: 'Remove User from Group', id: 'okta_remove_user_from_group' },
{ label: 'List Group Members', id: 'okta_list_group_members' },
],
value: () => 'okta_list_users',
},
{
id: 'apiKey',
title: 'API Token',
type: 'short-input',
password: true,
placeholder: 'Enter your Okta API token',
required: true,
},
{
id: 'domain',
title: 'Okta Domain',
type: 'short-input',
placeholder: 'dev-123456.okta.com',
required: true,
},
// Search/Filter params (list operations)
{
id: 'search',
title: 'Search',
type: 'short-input',
placeholder: 'profile.firstName eq "John"',
condition: { field: 'operation', value: ['okta_list_users', 'okta_list_groups'] },
},
{
id: 'filter',
title: 'Filter',
type: 'short-input',
placeholder: 'status eq "ACTIVE"',
condition: { field: 'operation', value: ['okta_list_users', 'okta_list_groups'] },
mode: 'advanced',
},
// User ID (shared across user operations that need it)
{
id: 'userId',
title: 'User ID',
type: 'short-input',
placeholder: 'User ID or login (email)',
condition: {
field: 'operation',
value: [
'okta_get_user',
'okta_update_user',
'okta_activate_user',
'okta_deactivate_user',
'okta_suspend_user',
'okta_unsuspend_user',
'okta_reset_password',
'okta_delete_user',
'okta_add_user_to_group',
'okta_remove_user_from_group',
],
},
required: {
field: 'operation',
value: [
'okta_get_user',
'okta_update_user',
'okta_activate_user',
'okta_deactivate_user',
'okta_suspend_user',
'okta_unsuspend_user',
'okta_reset_password',
'okta_delete_user',
'okta_add_user_to_group',
'okta_remove_user_from_group',
],
},
},
// Group ID (shared across group operations that need it)
{
id: 'groupId',
title: 'Group ID',
type: 'short-input',
placeholder: 'Okta group ID',
condition: {
field: 'operation',
value: [
'okta_get_group',
'okta_update_group',
'okta_delete_group',
'okta_add_user_to_group',
'okta_remove_user_from_group',
'okta_list_group_members',
],
},
required: {
field: 'operation',
value: [
'okta_get_group',
'okta_update_group',
'okta_delete_group',
'okta_add_user_to_group',
'okta_remove_user_from_group',
'okta_list_group_members',
],
},
},
// Create/Update User profile params
{
id: 'firstName',
title: 'First Name',
type: 'short-input',
placeholder: 'John',
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
required: { field: 'operation', value: 'okta_create_user' },
},
{
id: 'lastName',
title: 'Last Name',
type: 'short-input',
placeholder: 'Doe',
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
required: { field: 'operation', value: 'okta_create_user' },
},
{
id: 'email',
title: 'Email',
type: 'short-input',
placeholder: 'john.doe@example.com',
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
required: { field: 'operation', value: 'okta_create_user' },
},
{
id: 'login',
title: 'Login',
type: 'short-input',
placeholder: 'john.doe@example.com (defaults to email)',
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
mode: 'advanced',
},
{
id: 'password',
title: 'Password',
type: 'short-input',
password: true,
placeholder: 'Set user password',
condition: { field: 'operation', value: 'okta_create_user' },
mode: 'advanced',
},
{
id: 'mobilePhone',
title: 'Mobile Phone',
type: 'short-input',
placeholder: '+1234567890',
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
mode: 'advanced',
},
{
id: 'title',
title: 'Job Title',
type: 'short-input',
placeholder: 'Software Engineer',
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
mode: 'advanced',
},
{
id: 'department',
title: 'Department',
type: 'short-input',
placeholder: 'Engineering',
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
mode: 'advanced',
},
{
id: 'activate',
title: 'Activate Immediately',
type: 'switch',
condition: { field: 'operation', value: 'okta_create_user' },
mode: 'advanced',
},
// Group name (for create/update group)
{
id: 'groupName',
title: 'Group Name',
type: 'short-input',
placeholder: 'Engineering Team',
condition: { field: 'operation', value: ['okta_create_group', 'okta_update_group'] },
required: { field: 'operation', value: ['okta_create_group', 'okta_update_group'] },
},
{
id: 'groupDescription',
title: 'Group Description',
type: 'short-input',
placeholder: 'Description for the group',
condition: { field: 'operation', value: ['okta_create_group', 'okta_update_group'] },
},
// Send email option (activate, reset password, delete)
{
id: 'sendEmail',
title: 'Send Email',
type: 'switch',
condition: {
field: 'operation',
value: [
'okta_activate_user',
'okta_deactivate_user',
'okta_reset_password',
'okta_delete_user',
],
},
mode: 'advanced',
},
// Pagination
{
id: 'limit',
title: 'Limit',
type: 'short-input',
placeholder: 'Max results to return',
condition: {
field: 'operation',
value: ['okta_list_users', 'okta_list_groups', 'okta_list_group_members'],
},
mode: 'advanced',
},
],
tools: {
access: [
'okta_list_users',
'okta_get_user',
'okta_create_user',
'okta_update_user',
'okta_activate_user',
'okta_deactivate_user',
'okta_suspend_user',
'okta_unsuspend_user',
'okta_reset_password',
'okta_delete_user',
'okta_list_groups',
'okta_get_group',
'okta_create_group',
'okta_update_group',
'okta_delete_group',
'okta_add_user_to_group',
'okta_remove_user_from_group',
'okta_list_group_members',
],
config: {
tool: (params) => params.operation as string,
params: (params) => {
const result: Record<string, unknown> = {
apiKey: params.apiKey,
domain: params.domain,
}
if (params.limit) result.limit = Number(params.limit)
// Map group-specific UI fields to tool param names
if (params.groupName) result.name = params.groupName
if (params.groupDescription !== undefined) result.description = params.groupDescription
// Pass through all other non-empty params
// Allow empty strings so users can clear fields (e.g. update_user partial updates)
const skipKeys = new Set([
'operation',
'apiKey',
'domain',
'limit',
'groupName',
'groupDescription',
])
for (const [key, value] of Object.entries(params)) {
if (!skipKeys.has(key) && value !== undefined && value !== null) {
result[key] = value
}
}
return result
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'Okta API token' },
domain: { type: 'string', description: 'Okta domain' },
userId: { type: 'string', description: 'User ID or login' },
groupId: { type: 'string', description: 'Group ID' },
search: { type: 'string', description: 'Search expression' },
filter: { type: 'string', description: 'Filter expression' },
limit: { type: 'number', description: 'Max results to return' },
firstName: { type: 'string', description: 'First name' },
lastName: { type: 'string', description: 'Last name' },
email: { type: 'string', description: 'Email address' },
login: { type: 'string', description: 'Login (defaults to email)' },
password: { type: 'string', description: 'User password' },
mobilePhone: { type: 'string', description: 'Mobile phone number' },
title: { type: 'string', description: 'Job title' },
department: { type: 'string', description: 'Department' },
activate: { type: 'boolean', description: 'Activate user immediately on creation' },
groupName: { type: 'string', description: 'Group name' },
groupDescription: { type: 'string', description: 'Group description' },
sendEmail: { type: 'boolean', description: 'Whether to send email notification' },
},
outputs: {
users: {
type: 'json',
description:
'Array of user objects (id, status, firstName, lastName, email, login, mobilePhone, title, department, created, lastLogin, lastUpdated)',
},
members: {
type: 'json',
description:
'Array of group member user objects (id, status, firstName, lastName, email, login, mobilePhone, title, department, created, lastLogin, lastUpdated)',
},
groups: {
type: 'json',
description:
'Array of group objects (id, name, description, type, created, lastUpdated, lastMembershipUpdated)',
},
id: { type: 'string', description: 'Resource ID' },
status: { type: 'string', description: 'User status' },
firstName: { type: 'string', description: 'First name' },
lastName: { type: 'string', description: 'Last name' },
email: { type: 'string', description: 'Email address' },
login: { type: 'string', description: 'Login' },
name: { type: 'string', description: 'Group name' },
description: { type: 'string', description: 'Group description' },
type: { type: 'string', description: 'Group type' },
count: { type: 'number', description: 'Number of results' },
added: { type: 'boolean', description: 'Whether user was added to group' },
removed: { type: 'boolean', description: 'Whether user was removed from group' },
deactivated: { type: 'boolean', description: 'Whether user was deactivated' },
suspended: { type: 'boolean', description: 'Whether user was suspended' },
unsuspended: { type: 'boolean', description: 'Whether user was unsuspended' },
activated: { type: 'boolean', description: 'Whether user was activated' },
deleted: { type: 'boolean', description: 'Whether resource was deleted' },
activationUrl: { type: 'string', description: 'Activation URL (when sendEmail is false)' },
activationToken: { type: 'string', description: 'Activation token (when sendEmail is false)' },
resetPasswordUrl: {
type: 'string',
description: 'Password reset URL (when sendEmail is false)',
},
created: { type: 'string', description: 'Creation timestamp' },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+2
View File
@@ -122,6 +122,7 @@ import { Neo4jBlock } from '@/blocks/blocks/neo4j'
import { NoteBlock } from '@/blocks/blocks/note'
import { NotionBlock, NotionV2Block } from '@/blocks/blocks/notion'
import { ObsidianBlock } from '@/blocks/blocks/obsidian'
import { OktaBlock } from '@/blocks/blocks/okta'
import { OneDriveBlock } from '@/blocks/blocks/onedrive'
import { OnePasswordBlock } from '@/blocks/blocks/onepassword'
import { OpenAIBlock } from '@/blocks/blocks/openai'
@@ -340,6 +341,7 @@ export const registry: Record<string, BlockConfig> = {
notion: NotionBlock,
notion_v2: NotionV2Block,
obsidian: ObsidianBlock,
okta: OktaBlock,
onepassword: OnePasswordBlock,
onedrive: OneDriveBlock,
openai: OpenAIBlock,
+13
View File
@@ -6106,6 +6106,19 @@ export function AgentSkillsIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function OktaIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 63 63' xmlns='http://www.w3.org/2000/svg'>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M34.6.4l-1.3 16c-.6-.1-1.2-.1-1.9-.1-.8 0-1.6.1-2.3.2l-.7-7.7c0-.2.2-.5.4-.5h1.3L29.5.5c0-.2.2-.5.4-.5h4.3c.3 0 .5.2.4.4zm-10.8.8c-.1-.2-.3-.4-.5-.3l-4 1.5c-.3.1-.4.4-.3.6l3.3 7.1-1.2.5c-.2.1-.3.3-.2.6l3.3 7c1.2-.7 2.5-1.2 3.9-1.5L23.8 1.2zM14 5.7l9.3 13.1c-1.2.8-2.2 1.7-3.1 2.7L14.5 16c-.2-.2-.2-.5 0-.6l1-.8L10 9c-.2-.2-.2-.5 0-.6l3.3-2.7c.2-.3.5-.2.7 0zM6.2 13.2c-.2-.1-.5-.1-.6.1l-2.1 3.7c-.1.2 0 .5.2.6l7.1 3.4-.7 1.1c-.1.2 0 .5.2.6l7.1 3.2c.5-1.3 1.2-2.5 2-3.6L6.2 13.2zM.9 23.3c0-.2.3-.4.5-.3l15.5 4c-.4 1.3-.6 2.7-.7 4.1l-7.8-.6c-.2 0-.4-.2-.4-.5l.2-1.3L.6 28c-.2 0-.4-.2-.4-.5l.7-4.2zM.4 33.8c-.3 0-.4.2-.4.5l.8 4.2c0 .2.3.4.5.3l7.6-2 .2 1.3c0 .2.3.4.5.3l7.5-2.1c-.4-1.3-.7-2.7-.8-4.1L.4 33.8zm2.5 11.1c-.1-.2 0-.5.2-.6l14.5-6.9c.5 1.3 1.3 2.5 2.2 3.6l-6.3 4.5c-.2.1-.5.1-.6-.1L12 44.3l-6.5 4.5c-.2.1-.5.1-.6-.1l-2-3.8zm17.5-3L9.1 53.3c-.2.2-.2.5 0 .6l3.3 2.7c.2.2.5.1.6-.1l4.6-6.4 1 .9c.2.2.5.1.6-.1l4.4-6.4c-1.2-.7-2.3-1.6-3.2-2.6zm-2.2 18.2c-.2-.1-.3-.3-.2-.6L24.6 45c1.2.6 2.6 1.1 3.9 1.4l-2 7.5c-.1.2-.3.4-.5.3l-1.2-.5-2.1 7.6c-.1.2-.3.4-.5.3l-4-1.5zm10.9-13.5l-1.3 16c0 .2.2.5.4.5H33c.2 0 .4-.2.4-.5l-.6-7.8h1.3c.2 0 .4-.2.4-.5l-.7-7.7c-.8.1-1.5.2-2.3.2-.6 0-1.3 0-1.9-.1zm16-43.2c.1-.2 0-.5-.2-.6l-4-1.5c-.2-.1-.5.1-.5.3l-2.1 7.6-1.2-.5c-.2-.1-.5.1-.5.3l-2 7.5c1.4.3 2.7.8 3.9 1.4l6.6-14.5zm8.8 6.3L42.6 21.1c-.9-1-2-1.9-3.2-2.6l4.4-6.4c.1-.2.4-.2.6-.1l1 .9 4.6-6.4c.1-.2.4-.2.6-.1l3.3 2.7c.2.2.2.5 0 .6zM59.9 18.7c.2-.1.3-.4.2-.6L58 14.4c-.1-.2-.4-.3-.6-.1l-6.5 4.5-.7-1.1c-.1-.2-.4-.3-.6-.1L43.3 22c.9 1.1 1.6 2.3 2.2 3.6l14.4-6.9zm2.3 5.8l.7 4.2c0 .2-.1.5-.4.5l-15.9 1.5c-.1-1.4-.4-2.8-.8-4.1l7.5-2.1c.2-.1.5.1.5.3l.2 1.3 7.6-2c.3-.1.5.1.6.4zM61.5 40c.2.1.5-.1.5-.3l.7-4.2c0-.2-.1-.5-.4-.5l-7.8-.7.2-1.3c0-.2-.1-.5-.4-.5l-7.8-.6c0 1.4-.3 2.8-.7 4.1L61.5 40zm-4.1 9.6c-.1.2-.4.3-.6.1l-13.2-9.1c.8-1.1 1.5-2.3 2-3.6l7.1 3.2c.2.1.3.4.2.6L52.2 42l7.1 3.4c.2.1.3.4.2.6l-2.1 3.6zm-17.7-5.4L49 57.3c.1.2.4.2.6.1l3.3-2.7c.2-.2.2-.4 0-.6l-5.5-5.6 1-.8c.2-.2.2-.4 0-.6l-5.5-5.5c1.1.8 0 1.7-1.2 2.4zm0 17.8c-.2.1-.5-.1-.5-.3l-4.2-15.4c1.4-.3 2.7-.8 3.9-1.5l3.3 7c.1.2 0 .5-.2.6l-1.2.5 3.3 7.1c.1.2 0 .5-.2.6L39.7 62z'
fill='currentColor'
/>
</svg>
)
}
export function OnePasswordIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg' fill='none'>
@@ -1192,3 +1192,33 @@ export function validateCallbackUrl(url: string): boolean {
return false
}
}
const OKTA_DOMAIN_PATTERN =
/^[a-zA-Z0-9][a-zA-Z0-9-]*\.(okta|okta-gov|okta-emea|oktapreview|trexcloud)\.com$/
/**
* Validates and sanitizes an Okta domain to prevent SSRF.
* Ensures the domain matches a known Okta domain suffix.
*
* @param rawDomain - The raw domain string (may include protocol, trailing slash, or whitespace)
* @returns The cleaned, validated domain string
* @throws Error if the domain does not match a known Okta domain suffix
*
* @example
* ```typescript
* const domain = validateOktaDomain(params.domain)
* // Returns: "dev-123456.okta.com"
* ```
*/
export function validateOktaDomain(rawDomain: string): string {
const domain = rawDomain
.trim()
.replace(/^https?:\/\//, '')
.replace(/\/$/, '')
if (!OKTA_DOMAIN_PATTERN.test(domain)) {
throw new Error(
`Invalid Okta domain: "${domain}". Must be a valid Okta domain (e.g., dev-123456.okta.com)`
)
}
return domain
}
+109
View File
@@ -0,0 +1,109 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaActivateUserParams,
OktaActivateUserResponse,
OktaApiError,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaActivateUser')
export const oktaActivateUserTool: ToolConfig<OktaActivateUserParams, OktaActivateUserResponse> = {
id: 'okta_activate_user',
name: 'Activate User in Okta',
description:
'Activate a user in your Okta organization. Can only be performed on users with STAGED or DEPROVISIONED status. Optionally sends an activation email.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or login to activate',
},
sendEmail: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Send activation email to the user (default: true)',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
const sendEmail = params.sendEmail ?? true
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/activate?sendEmail=${sendEmail}`
},
method: 'POST',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to activate user in Okta')
}
let activationUrl: string | null = null
let activationToken: string | null = null
try {
const data = await response.json()
activationUrl = data.activationUrl ?? null
activationToken = data.activationToken ?? null
} catch {
// empty body when sendEmail=true
}
return {
success: true,
output: {
userId: params?.userId ?? '',
activated: true,
activationUrl,
activationToken,
success: true,
},
}
},
outputs: {
userId: { type: 'string', description: 'Activated user ID' },
activated: { type: 'boolean', description: 'Whether the user was activated' },
activationUrl: {
type: 'string',
description: 'Activation URL (only returned when sendEmail is false)',
optional: true,
},
activationToken: {
type: 'string',
description: 'Activation token (only returned when sendEmail is false)',
optional: true,
},
success: { type: 'boolean', description: 'Operation success status' },
},
}
+90
View File
@@ -0,0 +1,90 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaAddUserToGroupParams,
OktaAddUserToGroupResponse,
OktaApiError,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaAddUserToGroup')
export const oktaAddUserToGroupTool: ToolConfig<
OktaAddUserToGroupParams,
OktaAddUserToGroupResponse
> = {
id: 'okta_add_user_to_group',
name: 'Add User to Group in Okta',
description: 'Add a user to a group in your Okta organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID to add the user to',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID to add to the group',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}/users/${encodeURIComponent(params.userId)}`
},
method: 'PUT',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to add user to group in Okta')
}
return {
success: true,
output: {
groupId: params?.groupId ?? '',
userId: params?.userId ?? '',
added: true,
success: true,
},
}
},
outputs: {
groupId: { type: 'string', description: 'Group ID' },
userId: { type: 'string', description: 'User ID added to the group' },
added: { type: 'boolean', description: 'Whether the user was added' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+106
View File
@@ -0,0 +1,106 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaCreateGroupParams,
OktaCreateGroupResponse,
OktaGroup,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaCreateGroup')
export const oktaCreateGroupTool: ToolConfig<OktaCreateGroupParams, OktaCreateGroupResponse> = {
id: 'okta_create_group',
name: 'Create Group in Okta',
description: 'Create a new group in your Okta organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the group',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Description of the group',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/groups`
},
method: 'POST',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
body: (params) => {
const profile: Record<string, string> = { name: params.name }
if (params.description) profile.description = params.description
return { profile }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to create group in Okta')
}
const group: OktaGroup = await response.json()
return {
success: true,
output: {
id: group.id,
name: group.profile?.name ?? '',
description: group.profile?.description ?? null,
type: group.type,
created: group.created,
lastUpdated: group.lastUpdated,
lastMembershipUpdated: group.lastMembershipUpdated ?? null,
success: true,
},
}
},
outputs: {
id: { type: 'string', description: 'Created group ID' },
name: { type: 'string', description: 'Group name' },
description: { type: 'string', description: 'Group description', optional: true },
type: { type: 'string', description: 'Group type' },
created: { type: 'string', description: 'Creation timestamp' },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
lastMembershipUpdated: {
type: 'string',
description: 'Last membership change timestamp',
optional: true,
},
success: { type: 'boolean', description: 'Operation success status' },
},
}
+164
View File
@@ -0,0 +1,164 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaCreateUserParams,
OktaCreateUserResponse,
OktaUser,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaCreateUser')
export const oktaCreateUserTool: ToolConfig<OktaCreateUserParams, OktaCreateUserResponse> = {
id: 'okta_create_user',
name: 'Create User in Okta',
description: 'Create a new user in your Okta organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
firstName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'First name of the user',
},
lastName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Last name of the user',
},
email: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Email address of the user',
},
login: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Login for the user (defaults to email if not provided)',
},
password: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Password for the user (if not set, user will be emailed to set password)',
},
mobilePhone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Mobile phone number',
},
title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Job title',
},
department: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Department',
},
activate: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to activate the user immediately (default: true)',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
const activate = params.activate ?? true
return `https://${domain}/api/v1/users?activate=${activate}`
},
method: 'POST',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
body: (params) => {
const profile: Record<string, string> = {
firstName: params.firstName,
lastName: params.lastName,
email: params.email,
login: params.login || params.email,
}
if (params.mobilePhone) profile.mobilePhone = params.mobilePhone
if (params.title) profile.title = params.title
if (params.department) profile.department = params.department
const body: Record<string, unknown> = { profile }
if (params.password) {
body.credentials = {
password: { value: params.password },
}
}
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to create user in Okta')
}
const user: OktaUser = await response.json()
return {
success: true,
output: {
id: user.id,
status: user.status,
firstName: user.profile?.firstName ?? null,
lastName: user.profile?.lastName ?? null,
email: user.profile?.email ?? null,
login: user.profile?.login ?? null,
created: user.created,
lastUpdated: user.lastUpdated,
success: true,
},
}
},
outputs: {
id: { type: 'string', description: 'Created user ID' },
status: { type: 'string', description: 'User status' },
firstName: { type: 'string', description: 'First name', optional: true },
lastName: { type: 'string', description: 'Last name', optional: true },
email: { type: 'string', description: 'Email address', optional: true },
login: { type: 'string', description: 'Login', optional: true },
created: { type: 'string', description: 'Creation timestamp' },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+90
View File
@@ -0,0 +1,90 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaDeactivateUserParams,
OktaDeactivateUserResponse,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaDeactivateUser')
export const oktaDeactivateUserTool: ToolConfig<
OktaDeactivateUserParams,
OktaDeactivateUserResponse
> = {
id: 'okta_deactivate_user',
name: 'Deactivate User in Okta',
description:
'Deactivate a user in your Okta organization. This transitions the user to DEPROVISIONED status.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or login to deactivate',
},
sendEmail: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Send deactivation email to admin (default: false)',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
const sendEmail = params.sendEmail === true
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/deactivate?sendEmail=${sendEmail}`
},
method: 'POST',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body on some error codes
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to deactivate user in Okta')
}
return {
success: true,
output: {
userId: params?.userId ?? '',
deactivated: true,
success: true,
},
}
},
outputs: {
userId: { type: 'string', description: 'Deactivated user ID' },
deactivated: { type: 'boolean', description: 'Whether the user was deactivated' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+80
View File
@@ -0,0 +1,80 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaDeleteGroupParams,
OktaDeleteGroupResponse,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaDeleteGroup')
export const oktaDeleteGroupTool: ToolConfig<OktaDeleteGroupParams, OktaDeleteGroupResponse> = {
id: 'okta_delete_group',
name: 'Delete Group from Okta',
description:
'Delete a group from your Okta organization. Groups of OKTA_GROUP or APP_GROUP type can be removed.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID to delete',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}`
},
method: 'DELETE',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to delete group from Okta')
}
return {
success: true,
output: {
groupId: params?.groupId ?? '',
deleted: true,
success: true,
},
}
},
outputs: {
groupId: { type: 'string', description: 'Deleted group ID' },
deleted: { type: 'boolean', description: 'Whether the group was deleted' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+83
View File
@@ -0,0 +1,83 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type { OktaApiError, OktaDeleteUserParams, OktaDeleteUserResponse } from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaDeleteUser')
export const oktaDeleteUserTool: ToolConfig<OktaDeleteUserParams, OktaDeleteUserResponse> = {
id: 'okta_delete_user',
name: 'Delete User from Okta',
description:
'Permanently delete a user from your Okta organization. Can only be performed on DEPROVISIONED users. If the user is active, this will first deactivate them and a second call is needed to delete.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID to delete',
},
sendEmail: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Send deactivation email to admin (default: false)',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
const sendEmail = params.sendEmail === true
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}?sendEmail=${sendEmail}`
},
method: 'DELETE',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to delete user from Okta')
}
return {
success: true,
output: {
userId: params?.userId ?? '',
deleted: true,
success: true,
},
}
},
outputs: {
userId: { type: 'string', description: 'Deleted user ID' },
deleted: { type: 'boolean', description: 'Whether the user was deleted' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+95
View File
@@ -0,0 +1,95 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaGetGroupParams,
OktaGetGroupResponse,
OktaGroup,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaGetGroup')
export const oktaGetGroupTool: ToolConfig<OktaGetGroupParams, OktaGetGroupResponse> = {
id: 'okta_get_group',
name: 'Get Group from Okta',
description: 'Get a specific group by ID from your Okta organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID to look up',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}`
},
method: 'GET',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to get group from Okta')
}
const group: OktaGroup = await response.json()
return {
success: true,
output: {
id: group.id,
name: group.profile?.name ?? '',
description: group.profile?.description ?? null,
type: group.type,
created: group.created,
lastUpdated: group.lastUpdated,
lastMembershipUpdated: group.lastMembershipUpdated ?? null,
success: true,
},
}
},
outputs: {
id: { type: 'string', description: 'Group ID' },
name: { type: 'string', description: 'Group name' },
description: { type: 'string', description: 'Group description', optional: true },
type: { type: 'string', description: 'Group type' },
created: { type: 'string', description: 'Creation timestamp' },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
lastMembershipUpdated: {
type: 'string',
description: 'Last membership change timestamp',
optional: true,
},
success: { type: 'boolean', description: 'Operation success status' },
},
}
+123
View File
@@ -0,0 +1,123 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaGetUserParams,
OktaGetUserResponse,
OktaUser,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaGetUser')
export const oktaGetUserTool: ToolConfig<OktaGetUserParams, OktaGetUserResponse> = {
id: 'okta_get_user',
name: 'Get User from Okta',
description: 'Get a specific user by ID or login from your Okta organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or login (email) to look up',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}`
},
method: 'GET',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to get user from Okta')
}
const user: OktaUser = await response.json()
return {
success: true,
output: {
id: user.id,
status: user.status,
firstName: user.profile?.firstName ?? null,
lastName: user.profile?.lastName ?? null,
email: user.profile?.email ?? null,
login: user.profile?.login ?? null,
mobilePhone: user.profile?.mobilePhone ?? null,
secondEmail: user.profile?.secondEmail ?? null,
displayName: user.profile?.displayName ?? null,
title: user.profile?.title ?? null,
department: user.profile?.department ?? null,
organization: user.profile?.organization ?? null,
manager: user.profile?.manager ?? null,
managerId: user.profile?.managerId ?? null,
division: user.profile?.division ?? null,
employeeNumber: user.profile?.employeeNumber ?? null,
userType: user.profile?.userType ?? null,
created: user.created,
activated: user.activated ?? null,
lastLogin: user.lastLogin ?? null,
lastUpdated: user.lastUpdated,
statusChanged: user.statusChanged ?? null,
passwordChanged: user.passwordChanged ?? null,
success: true,
},
}
},
outputs: {
id: { type: 'string', description: 'User ID' },
status: { type: 'string', description: 'User status' },
firstName: { type: 'string', description: 'First name', optional: true },
lastName: { type: 'string', description: 'Last name', optional: true },
email: { type: 'string', description: 'Email address', optional: true },
login: { type: 'string', description: 'Login (usually email)', optional: true },
mobilePhone: { type: 'string', description: 'Mobile phone', optional: true },
secondEmail: { type: 'string', description: 'Secondary email', optional: true },
displayName: { type: 'string', description: 'Display name', optional: true },
title: { type: 'string', description: 'Job title', optional: true },
department: { type: 'string', description: 'Department', optional: true },
organization: { type: 'string', description: 'Organization', optional: true },
manager: { type: 'string', description: 'Manager name', optional: true },
managerId: { type: 'string', description: 'Manager ID', optional: true },
division: { type: 'string', description: 'Division', optional: true },
employeeNumber: { type: 'string', description: 'Employee number', optional: true },
userType: { type: 'string', description: 'User type', optional: true },
created: { type: 'string', description: 'Creation timestamp' },
activated: { type: 'string', description: 'Activation timestamp', optional: true },
lastLogin: { type: 'string', description: 'Last login timestamp', optional: true },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
statusChanged: { type: 'string', description: 'Status change timestamp', optional: true },
passwordChanged: { type: 'string', description: 'Password change timestamp', optional: true },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+19
View File
@@ -0,0 +1,19 @@
export { oktaActivateUserTool } from './activate_user'
export { oktaAddUserToGroupTool } from './add_user_to_group'
export { oktaCreateGroupTool } from './create_group'
export { oktaCreateUserTool } from './create_user'
export { oktaDeactivateUserTool } from './deactivate_user'
export { oktaDeleteGroupTool } from './delete_group'
export { oktaDeleteUserTool } from './delete_user'
export { oktaGetGroupTool } from './get_group'
export { oktaGetUserTool } from './get_user'
export { oktaListGroupMembersTool } from './list_group_members'
export { oktaListGroupsTool } from './list_groups'
export { oktaListUsersTool } from './list_users'
export { oktaRemoveUserFromGroupTool } from './remove_user_from_group'
export { oktaResetPasswordTool } from './reset_password'
export { oktaSuspendUserTool } from './suspend_user'
export * from './types'
export { oktaUnsuspendUserTool } from './unsuspend_user'
export { oktaUpdateGroupTool } from './update_group'
export { oktaUpdateUserTool } from './update_user'
+140
View File
@@ -0,0 +1,140 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaListGroupMembersParams,
OktaListGroupMembersResponse,
OktaUser,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaListGroupMembers')
export const oktaListGroupMembersTool: ToolConfig<
OktaListGroupMembersParams,
OktaListGroupMembersResponse
> = {
id: 'okta_list_group_members',
name: 'List Group Members from Okta',
description: 'List all members of a specific group in your Okta organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID to list members for',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of members to return (default: 1000, max: 1000)',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
const queryParams = new URLSearchParams()
if (params.limit) queryParams.append('limit', params.limit.toString())
const queryString = queryParams.toString()
const base = `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}/users`
return queryString ? `${base}?${queryString}` : base
},
method: 'GET',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to list group members from Okta')
}
const data: OktaUser[] = await response.json()
const members = data.map((user) => ({
id: user.id,
status: user.status,
firstName: user.profile?.firstName ?? null,
lastName: user.profile?.lastName ?? null,
email: user.profile?.email ?? null,
login: user.profile?.login ?? null,
mobilePhone: user.profile?.mobilePhone ?? null,
title: user.profile?.title ?? null,
department: user.profile?.department ?? null,
created: user.created,
lastLogin: user.lastLogin ?? null,
lastUpdated: user.lastUpdated,
activated: user.activated ?? null,
statusChanged: user.statusChanged ?? null,
}))
return {
success: true,
output: {
members,
count: members.length,
success: true,
},
}
},
outputs: {
members: {
type: 'array',
description: 'Array of group member user objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'User ID' },
status: { type: 'string', description: 'User status' },
firstName: { type: 'string', description: 'First name', optional: true },
lastName: { type: 'string', description: 'Last name', optional: true },
email: { type: 'string', description: 'Email address', optional: true },
login: { type: 'string', description: 'Login', optional: true },
mobilePhone: { type: 'string', description: 'Mobile phone', optional: true },
title: { type: 'string', description: 'Job title', optional: true },
department: { type: 'string', description: 'Department', optional: true },
created: { type: 'string', description: 'Creation timestamp' },
lastLogin: { type: 'string', description: 'Last login timestamp', optional: true },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
activated: { type: 'string', description: 'Activation timestamp', optional: true },
statusChanged: {
type: 'string',
description: 'Status change timestamp',
optional: true,
},
},
},
},
count: { type: 'number', description: 'Number of members returned' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+133
View File
@@ -0,0 +1,133 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaGroup,
OktaListGroupsParams,
OktaListGroupsResponse,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaListGroups')
export const oktaListGroupsTool: ToolConfig<OktaListGroupsParams, OktaListGroupsResponse> = {
id: 'okta_list_groups',
name: 'List Groups from Okta',
description: 'List all groups in your Okta organization with optional search and filtering',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Okta search expression for groups (e.g., profile.name sw "Engineering" or type eq "OKTA_GROUP")',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Okta filter expression (e.g., type eq "OKTA_GROUP")',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of groups to return (default: 10000, max: 10000)',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
const queryParams = new URLSearchParams()
if (params.search) queryParams.append('search', params.search)
if (params.filter) queryParams.append('filter', params.filter)
if (params.limit) queryParams.append('limit', params.limit.toString())
const queryString = queryParams.toString()
return queryString
? `https://${domain}/api/v1/groups?${queryString}`
: `https://${domain}/api/v1/groups`
},
method: 'GET',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to list groups from Okta')
}
const data: OktaGroup[] = await response.json()
const groups = data.map((group) => ({
id: group.id,
name: group.profile?.name ?? '',
description: group.profile?.description ?? null,
type: group.type,
created: group.created,
lastUpdated: group.lastUpdated,
lastMembershipUpdated: group.lastMembershipUpdated ?? null,
}))
return {
success: true,
output: {
groups,
count: groups.length,
success: true,
},
}
},
outputs: {
groups: {
type: 'array',
description: 'Array of Okta group objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Group ID' },
name: { type: 'string', description: 'Group name' },
description: { type: 'string', description: 'Group description', optional: true },
type: { type: 'string', description: 'Group type (OKTA_GROUP, APP_GROUP, BUILT_IN)' },
created: { type: 'string', description: 'Creation timestamp' },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
lastMembershipUpdated: {
type: 'string',
description: 'Last membership change timestamp',
optional: true,
},
},
},
},
count: { type: 'number', description: 'Number of groups returned' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+146
View File
@@ -0,0 +1,146 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaListUsersParams,
OktaListUsersResponse,
OktaUser,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaListUsers')
export const oktaListUsersTool: ToolConfig<OktaListUsersParams, OktaListUsersResponse> = {
id: 'okta_list_users',
name: 'List Users from Okta',
description: 'List all users in your Okta organization with optional search and filtering',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Okta search expression (e.g., profile.firstName eq "John" or profile.email co "example.com")',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Okta filter expression (e.g., status eq "ACTIVE")',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of users to return (default: 200, max: 200)',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
const queryParams = new URLSearchParams()
if (params.search) queryParams.append('search', params.search)
if (params.filter) queryParams.append('filter', params.filter)
if (params.limit) queryParams.append('limit', params.limit.toString())
const queryString = queryParams.toString()
return queryString
? `https://${domain}/api/v1/users?${queryString}`
: `https://${domain}/api/v1/users`
},
method: 'GET',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to list users from Okta')
}
const data: OktaUser[] = await response.json()
const users = data.map((user) => ({
id: user.id,
status: user.status,
firstName: user.profile?.firstName ?? null,
lastName: user.profile?.lastName ?? null,
email: user.profile?.email ?? null,
login: user.profile?.login ?? null,
mobilePhone: user.profile?.mobilePhone ?? null,
title: user.profile?.title ?? null,
department: user.profile?.department ?? null,
created: user.created,
lastLogin: user.lastLogin ?? null,
lastUpdated: user.lastUpdated,
activated: user.activated ?? null,
statusChanged: user.statusChanged ?? null,
}))
return {
success: true,
output: {
users,
count: users.length,
success: true,
},
}
},
outputs: {
users: {
type: 'array',
description: 'Array of Okta user objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'User ID' },
status: {
type: 'string',
description: 'User status (ACTIVE, STAGED, PROVISIONED, etc.)',
},
firstName: { type: 'string', description: 'First name', optional: true },
lastName: { type: 'string', description: 'Last name', optional: true },
email: { type: 'string', description: 'Email address', optional: true },
login: { type: 'string', description: 'Login (usually email)', optional: true },
mobilePhone: { type: 'string', description: 'Mobile phone', optional: true },
title: { type: 'string', description: 'Job title', optional: true },
department: { type: 'string', description: 'Department', optional: true },
created: { type: 'string', description: 'Creation timestamp' },
lastLogin: { type: 'string', description: 'Last login timestamp', optional: true },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
activated: { type: 'string', description: 'Activation timestamp', optional: true },
statusChanged: { type: 'string', description: 'Status change timestamp', optional: true },
},
},
},
count: { type: 'number', description: 'Number of users returned' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
@@ -0,0 +1,90 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaRemoveUserFromGroupParams,
OktaRemoveUserFromGroupResponse,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaRemoveUserFromGroup')
export const oktaRemoveUserFromGroupTool: ToolConfig<
OktaRemoveUserFromGroupParams,
OktaRemoveUserFromGroupResponse
> = {
id: 'okta_remove_user_from_group',
name: 'Remove User from Group in Okta',
description: 'Remove a user from a group in your Okta organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID to remove the user from',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID to remove from the group',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}/users/${encodeURIComponent(params.userId)}`
},
method: 'DELETE',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to remove user from group in Okta')
}
return {
success: true,
output: {
groupId: params?.groupId ?? '',
userId: params?.userId ?? '',
removed: true,
success: true,
},
}
},
outputs: {
groupId: { type: 'string', description: 'Group ID' },
userId: { type: 'string', description: 'User ID removed from the group' },
removed: { type: 'boolean', description: 'Whether the user was removed' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+100
View File
@@ -0,0 +1,100 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaResetPasswordParams,
OktaResetPasswordResponse,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaResetPassword')
export const oktaResetPasswordTool: ToolConfig<OktaResetPasswordParams, OktaResetPasswordResponse> =
{
id: 'okta_reset_password',
name: 'Reset Password in Okta',
description:
'Generate a one-time token to reset a user password. Can email the reset link to the user or return it directly. Transitions the user to RECOVERY status.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or login to reset password for',
},
sendEmail: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Send password reset email to the user (default: true)',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
const sendEmail = params.sendEmail ?? true
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/reset_password?sendEmail=${sendEmail}`
},
method: 'POST',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to reset password in Okta')
}
let resetPasswordUrl: string | null = null
try {
const data = await response.json()
resetPasswordUrl = data.resetPasswordUrl ?? null
} catch {
// empty body when sendEmail=true
}
return {
success: true,
output: {
userId: params?.userId ?? '',
resetPasswordUrl,
success: true,
},
}
},
outputs: {
userId: { type: 'string', description: 'User ID' },
resetPasswordUrl: {
type: 'string',
description: 'Password reset URL (only returned when sendEmail is false)',
optional: true,
},
success: { type: 'boolean', description: 'Operation success status' },
},
}
+80
View File
@@ -0,0 +1,80 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaSuspendUserParams,
OktaSuspendUserResponse,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaSuspendUser')
export const oktaSuspendUserTool: ToolConfig<OktaSuspendUserParams, OktaSuspendUserResponse> = {
id: 'okta_suspend_user',
name: 'Suspend User in Okta',
description:
'Suspend a user in your Okta organization. Only users with ACTIVE status can be suspended. Suspended users cannot log in but retain group and app assignments.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or login to suspend',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/suspend`
},
method: 'POST',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to suspend user in Okta')
}
return {
success: true,
output: {
userId: params?.userId ?? '',
suspended: true,
success: true,
},
}
},
outputs: {
userId: { type: 'string', description: 'Suspended user ID' },
suspended: { type: 'boolean', description: 'Whether the user was suspended' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+446
View File
@@ -0,0 +1,446 @@
import type { ToolResponse } from '@/tools/types'
/**
* Okta API error response
*/
export interface OktaApiError {
errorCode?: string
errorSummary?: string
errorCauses?: { errorSummary: string }[]
}
/**
* Common params for all Okta tools
*/
export interface OktaBaseParams {
apiKey: string
domain: string
}
/**
* Okta User profile object from the API
*/
export interface OktaUserProfile {
firstName?: string | null
lastName?: string | null
email?: string | null
login?: string | null
mobilePhone?: string | null
secondEmail?: string | null
displayName?: string | null
nickName?: string | null
title?: string | null
department?: string | null
organization?: string | null
manager?: string | null
managerId?: string | null
division?: string | null
costCenter?: string | null
employeeNumber?: string | null
userType?: string | null
}
/**
* Okta User object from the API
*/
export interface OktaUser {
id: string
status: string
created: string
activated: string | null
statusChanged: string | null
lastLogin: string | null
lastUpdated: string
passwordChanged: string | null
type: { id: string }
profile: OktaUserProfile
}
/**
* Okta Group profile from the API
*/
export interface OktaGroupProfile {
name: string
description?: string | null
}
/**
* Okta Group object from the API
*/
export interface OktaGroup {
id: string
created: string
lastUpdated: string
lastMembershipUpdated: string | null
type: string
profile: OktaGroupProfile
}
/**
* Transformed user output
*/
export interface OktaUserOutput {
id: string
status: string
firstName: string | null
lastName: string | null
email: string | null
login: string | null
mobilePhone: string | null
title: string | null
department: string | null
created: string
lastLogin: string | null
lastUpdated: string
activated: string | null
statusChanged: string | null
}
/**
* Transformed group output
*/
export interface OktaGroupOutput {
id: string
name: string
description: string | null
type: string
created: string
lastUpdated: string
lastMembershipUpdated: string | null
}
// List Users
export interface OktaListUsersParams extends OktaBaseParams {
search?: string
filter?: string
limit?: number
}
export interface OktaListUsersResponse extends ToolResponse {
output: {
users: OktaUserOutput[]
count: number
success: boolean
}
}
// Get User
export interface OktaGetUserParams extends OktaBaseParams {
userId: string
}
export interface OktaGetUserResponse extends ToolResponse {
output: {
id: string
status: string
firstName: string | null
lastName: string | null
email: string | null
login: string | null
mobilePhone: string | null
secondEmail: string | null
displayName: string | null
title: string | null
department: string | null
organization: string | null
manager: string | null
managerId: string | null
division: string | null
employeeNumber: string | null
userType: string | null
created: string
activated: string | null
lastLogin: string | null
lastUpdated: string
statusChanged: string | null
passwordChanged: string | null
success: boolean
}
}
// Create User
export interface OktaCreateUserParams extends OktaBaseParams {
firstName: string
lastName: string
email: string
login?: string
password?: string
mobilePhone?: string
title?: string
department?: string
activate?: boolean
}
export interface OktaCreateUserResponse extends ToolResponse {
output: {
id: string
status: string
firstName: string | null
lastName: string | null
email: string | null
login: string | null
created: string
lastUpdated: string
success: boolean
}
}
// Update User
export interface OktaUpdateUserParams extends OktaBaseParams {
userId: string
firstName?: string
lastName?: string
email?: string
login?: string
mobilePhone?: string
title?: string
department?: string
}
export interface OktaUpdateUserResponse extends ToolResponse {
output: {
id: string
status: string
firstName: string | null
lastName: string | null
email: string | null
login: string | null
created: string
lastUpdated: string
success: boolean
}
}
// Deactivate User
export interface OktaDeactivateUserParams extends OktaBaseParams {
userId: string
sendEmail?: boolean
}
export interface OktaDeactivateUserResponse extends ToolResponse {
output: {
userId: string
deactivated: boolean
success: boolean
}
}
// List Groups
export interface OktaListGroupsParams extends OktaBaseParams {
search?: string
filter?: string
limit?: number
}
export interface OktaListGroupsResponse extends ToolResponse {
output: {
groups: OktaGroupOutput[]
count: number
success: boolean
}
}
// Get Group
export interface OktaGetGroupParams extends OktaBaseParams {
groupId: string
}
export interface OktaGetGroupResponse extends ToolResponse {
output: {
id: string
name: string
description: string | null
type: string
created: string
lastUpdated: string
lastMembershipUpdated: string | null
success: boolean
}
}
// Add User to Group
export interface OktaAddUserToGroupParams extends OktaBaseParams {
groupId: string
userId: string
}
export interface OktaAddUserToGroupResponse extends ToolResponse {
output: {
groupId: string
userId: string
added: boolean
success: boolean
}
}
// Remove User from Group
export interface OktaRemoveUserFromGroupParams extends OktaBaseParams {
groupId: string
userId: string
}
export interface OktaRemoveUserFromGroupResponse extends ToolResponse {
output: {
groupId: string
userId: string
removed: boolean
success: boolean
}
}
// List Group Members
export interface OktaListGroupMembersParams extends OktaBaseParams {
groupId: string
limit?: number
}
export interface OktaListGroupMembersResponse extends ToolResponse {
output: {
members: OktaUserOutput[]
count: number
success: boolean
}
}
// Suspend User
export interface OktaSuspendUserParams extends OktaBaseParams {
userId: string
}
export interface OktaSuspendUserResponse extends ToolResponse {
output: {
userId: string
suspended: boolean
success: boolean
}
}
// Unsuspend User
export interface OktaUnsuspendUserParams extends OktaBaseParams {
userId: string
}
export interface OktaUnsuspendUserResponse extends ToolResponse {
output: {
userId: string
unsuspended: boolean
success: boolean
}
}
// Activate User
export interface OktaActivateUserParams extends OktaBaseParams {
userId: string
sendEmail?: boolean
}
export interface OktaActivateUserResponse extends ToolResponse {
output: {
userId: string
activated: boolean
activationUrl: string | null
activationToken: string | null
success: boolean
}
}
// Reset Password
export interface OktaResetPasswordParams extends OktaBaseParams {
userId: string
sendEmail?: boolean
}
export interface OktaResetPasswordResponse extends ToolResponse {
output: {
userId: string
resetPasswordUrl: string | null
success: boolean
}
}
// Delete User
export interface OktaDeleteUserParams extends OktaBaseParams {
userId: string
sendEmail?: boolean
}
export interface OktaDeleteUserResponse extends ToolResponse {
output: {
userId: string
deleted: boolean
success: boolean
}
}
// Create Group
export interface OktaCreateGroupParams extends OktaBaseParams {
name: string
description?: string
}
export interface OktaCreateGroupResponse extends ToolResponse {
output: {
id: string
name: string
description: string | null
type: string
created: string
lastUpdated: string
lastMembershipUpdated: string | null
success: boolean
}
}
// Update Group
export interface OktaUpdateGroupParams extends OktaBaseParams {
groupId: string
name: string
description?: string
}
export interface OktaUpdateGroupResponse extends ToolResponse {
output: {
id: string
name: string
description: string | null
type: string
created: string
lastUpdated: string
lastMembershipUpdated: string | null
success: boolean
}
}
// Delete Group
export interface OktaDeleteGroupParams extends OktaBaseParams {
groupId: string
}
export interface OktaDeleteGroupResponse extends ToolResponse {
output: {
groupId: string
deleted: boolean
success: boolean
}
}
// Generic response type for the block
export type OktaResponse =
| OktaListUsersResponse
| OktaGetUserResponse
| OktaCreateUserResponse
| OktaUpdateUserResponse
| OktaDeactivateUserResponse
| OktaSuspendUserResponse
| OktaUnsuspendUserResponse
| OktaActivateUserResponse
| OktaResetPasswordResponse
| OktaDeleteUserResponse
| OktaListGroupsResponse
| OktaGetGroupResponse
| OktaCreateGroupResponse
| OktaUpdateGroupResponse
| OktaDeleteGroupResponse
| OktaAddUserToGroupResponse
| OktaRemoveUserFromGroupResponse
| OktaListGroupMembersResponse
+81
View File
@@ -0,0 +1,81 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaUnsuspendUserParams,
OktaUnsuspendUserResponse,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaUnsuspendUser')
export const oktaUnsuspendUserTool: ToolConfig<OktaUnsuspendUserParams, OktaUnsuspendUserResponse> =
{
id: 'okta_unsuspend_user',
name: 'Unsuspend User in Okta',
description:
'Unsuspend a previously suspended user in your Okta organization. Returns the user to ACTIVE status.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or login to unsuspend',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/unsuspend`
},
method: 'POST',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// empty response body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to unsuspend user in Okta')
}
return {
success: true,
output: {
userId: params?.userId ?? '',
unsuspended: true,
success: true,
},
}
},
outputs: {
userId: { type: 'string', description: 'Unsuspended user ID' },
unsuspended: { type: 'boolean', description: 'Whether the user was unsuspended' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+114
View File
@@ -0,0 +1,114 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaGroup,
OktaUpdateGroupParams,
OktaUpdateGroupResponse,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaUpdateGroup')
export const oktaUpdateGroupTool: ToolConfig<OktaUpdateGroupParams, OktaUpdateGroupResponse> = {
id: 'okta_update_group',
name: 'Update Group in Okta',
description:
'Update a group profile in your Okta organization. Only groups of OKTA_GROUP type can be updated. All profile properties must be specified (full replacement).',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID to update',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Updated group name',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated group description',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}`
},
method: 'PUT',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
body: (params) => ({
profile: {
name: params.name,
description: params.description ?? '',
},
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to update group in Okta')
}
const group: OktaGroup = await response.json()
return {
success: true,
output: {
id: group.id,
name: group.profile?.name ?? '',
description: group.profile?.description ?? null,
type: group.type,
created: group.created,
lastUpdated: group.lastUpdated,
lastMembershipUpdated: group.lastMembershipUpdated ?? null,
success: true,
},
}
},
outputs: {
id: { type: 'string', description: 'Group ID' },
name: { type: 'string', description: 'Group name' },
description: { type: 'string', description: 'Group description', optional: true },
type: { type: 'string', description: 'Group type' },
created: { type: 'string', description: 'Creation timestamp' },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
lastMembershipUpdated: {
type: 'string',
description: 'Last membership change timestamp',
optional: true,
},
success: { type: 'boolean', description: 'Operation success status' },
},
}
+148
View File
@@ -0,0 +1,148 @@
import { createLogger } from '@sim/logger'
import { validateOktaDomain } from '@/lib/core/security/input-validation'
import type {
OktaApiError,
OktaUpdateUserParams,
OktaUpdateUserResponse,
OktaUser,
} from '@/tools/okta/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('OktaUpdateUser')
export const oktaUpdateUserTool: ToolConfig<OktaUpdateUserParams, OktaUpdateUserResponse> = {
id: 'okta_update_user',
name: 'Update User in Okta',
description: 'Update a user profile in your Okta organization',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta API token for authentication',
},
domain: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Okta domain (e.g., dev-123456.okta.com)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or login to update',
},
firstName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated first name',
},
lastName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated last name',
},
email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated email address',
},
login: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated login',
},
mobilePhone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated mobile phone number',
},
title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated job title',
},
department: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated department',
},
},
request: {
url: (params) => {
const domain = validateOktaDomain(params.domain)
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}`
},
method: 'POST',
headers: (params) => ({
Authorization: `SSWS ${params.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}),
body: (params) => {
const profile: Record<string, string> = {}
if (params.firstName !== undefined) profile.firstName = params.firstName
if (params.lastName !== undefined) profile.lastName = params.lastName
if (params.email !== undefined) profile.email = params.email
if (params.login !== undefined) profile.login = params.login
if (params.mobilePhone !== undefined) profile.mobilePhone = params.mobilePhone
if (params.title !== undefined) profile.title = params.title
if (params.department !== undefined) profile.department = params.department
return { profile }
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
let error: OktaApiError = {}
try {
error = await response.json()
} catch {
// non-JSON error body
}
logger.error('Okta API request failed', { data: error, status: response.status })
throw new Error(error.errorSummary || 'Failed to update user in Okta')
}
const user: OktaUser = await response.json()
return {
success: true,
output: {
id: user.id,
status: user.status,
firstName: user.profile?.firstName ?? null,
lastName: user.profile?.lastName ?? null,
email: user.profile?.email ?? null,
login: user.profile?.login ?? null,
created: user.created,
lastUpdated: user.lastUpdated,
success: true,
},
}
},
outputs: {
id: { type: 'string', description: 'User ID' },
status: { type: 'string', description: 'User status' },
firstName: { type: 'string', description: 'First name', optional: true },
lastName: { type: 'string', description: 'Last name', optional: true },
email: { type: 'string', description: 'Email address', optional: true },
login: { type: 'string', description: 'Login', optional: true },
created: { type: 'string', description: 'Creation timestamp' },
lastUpdated: { type: 'string', description: 'Last update timestamp' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+38
View File
@@ -1574,6 +1574,26 @@ import {
obsidianPatchNoteTool,
obsidianSearchTool,
} from '@/tools/obsidian'
import {
oktaActivateUserTool,
oktaAddUserToGroupTool,
oktaCreateGroupTool,
oktaCreateUserTool,
oktaDeactivateUserTool,
oktaDeleteGroupTool,
oktaDeleteUserTool,
oktaGetGroupTool,
oktaGetUserTool,
oktaListGroupMembersTool,
oktaListGroupsTool,
oktaListUsersTool,
oktaRemoveUserFromGroupTool,
oktaResetPasswordTool,
oktaSuspendUserTool,
oktaUnsuspendUserTool,
oktaUpdateGroupTool,
oktaUpdateUserTool,
} from '@/tools/okta'
import {
onedriveCreateFolderTool,
onedriveDeleteTool,
@@ -2920,6 +2940,24 @@ export const tools: Record<string, ToolConfig> = {
obsidian_patch_active: obsidianPatchActiveTool,
obsidian_patch_note: obsidianPatchNoteTool,
obsidian_search: obsidianSearchTool,
okta_list_users: oktaListUsersTool,
okta_get_user: oktaGetUserTool,
okta_create_user: oktaCreateUserTool,
okta_update_user: oktaUpdateUserTool,
okta_activate_user: oktaActivateUserTool,
okta_deactivate_user: oktaDeactivateUserTool,
okta_suspend_user: oktaSuspendUserTool,
okta_unsuspend_user: oktaUnsuspendUserTool,
okta_reset_password: oktaResetPasswordTool,
okta_delete_user: oktaDeleteUserTool,
okta_list_groups: oktaListGroupsTool,
okta_get_group: oktaGetGroupTool,
okta_create_group: oktaCreateGroupTool,
okta_update_group: oktaUpdateGroupTool,
okta_delete_group: oktaDeleteGroupTool,
okta_add_user_to_group: oktaAddUserToGroupTool,
okta_remove_user_from_group: oktaRemoveUserFromGroupTool,
okta_list_group_members: oktaListGroupMembersTool,
onepassword_list_vaults: onepasswordListVaultsTool,
onepassword_get_vault: onepasswordGetVaultTool,
onepassword_list_items: onepasswordListItemsTool,