Okta v2

Integration with Okta's cloud-based identity management service.

Authentication & Identity Management · Okta

Details

IDOkta v2
ProviderOkta
CategoryAuthentication & Identity Management
From Version5.0.0
Docker Imagedemisto/crypto:1.0.0.5490413
Supported ModulesAgentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud

README

Integration with Okta’s cloud-based identity management service.

Configure Okta v2 in Cortex

API Token Authentication Prerequisites

  1. Sign in to your Okta organization as a user with administrator privileges.
  2. On the Admin Console, select Security > API from the menu, and then select the Tokens tab.
  3. Click Create Token.
  4. Name your token and click Create Token.

Notes

  • API tokens have the same permissions as the user who creates them, and if the permissions of a user change, so do the permissions of the API token.
  • If more than one certificate is assigned to the application, the Key ID parameter is required to specify which
    certificate to use for signing the JWT token.

For more information, see the ‘Create an API token’ official documentation article.

OAuth 2.0 Authentication Prerequisites

Required Scopes

The following scopes are required for the Okta v2 integration to work properly:

  • okta.apps.manage
  • okta.apps.read
  • okta.groups.manage
  • okta.groups.read
  • okta.logs.read
  • okta.networkZones.manage
  • okta.networkZones.read
  • okta.sessions.manage
  • okta.sessions.read
  • okta.users.manage
  • okta.users.read
  1. Sign in to Okta Admin Console.
  2. In the Admin Console, go to Applications > Applications.
  3. Click Create App Integration.
  4. Select API Services as the sign-in method, and click Next.
  5. Enter the desired name for the created app (e.g., “Cortex XSOAR”), and click Save.
  6. In the app configuration page, under the General tab and the Client Credentials section, select Public key / Private key for the Client authentication option.
  7. Under the newly added PUBLIC KEYS section, click Add Key.
  8. In the Add Public Key dialog box, click Generate new key. Make sure to copy the generated private key (in PEM format) to somewhere safe, and click Save.
  9. Under the General Settings section:
    1. Next to the Proof of possession label, uncheck the Require Demonstrating Proof of Possession (DPoP) header in token requests option if it’s selected.
    2. Next to the Grant type label, make sure the Client Credentials option is selected, and that the Token Exchange option is not selected.
    3. Click Save.
  10. Under the Okta API Scopes tab, grant the required scopes mentioned above for the app.
  11. Under the Admin roles tab:
    1. Click Edit assignments.
    2. In the dropdown list under “Role”, select Super Administrator.
    3. Click Save changes at the top.

For more information, see the ‘Implement OAuth for Okta’ official documentation article.

Note: OAuth 2.0 authentication is confirmed to support the ‘Revoke all user sessions’ functionality. When using the okta-clear-user-sessions command with revoke_oauth_tokens=true, it revokes OpenID Connect and OAuth refresh and access tokens issued to the user.

Instance Configuration

Parameter Description Required
Okta URL (https://<domain>.okta.com)   True
API Token   False
Use OAuth 2.0 Authentication See detailed instructions on the ‘Help’ tab. False
Client ID Required and used if OAuth 2.0 is used for authentication. See detailed instructions on the ‘Help’ tab. False
Private Key In PEM format. Required and used if OAuth 2.0 is used for authentication. See detailed instructions on the ‘Help’ tab. False
JWT Signing Algorithm Algorithm to sign generated JWT tokens with. Doesn’t affect integration’s functionality. Required and used if OAuth 2.0 is used for authentication. See detailed instructions on the ‘Help’ tab. False
Key ID Required and used if more than one key is used for signing JWT tokens. False
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

okta-unlock-user


Unlocks a single user.

Base Command

okta-unlock-user

Input

Argument Name Description Required
username Username to unlock. Required

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-unlock-user username=testForDocs@test.com

Human Readable Output

User testForDocs@test.com unlocked

okta-deactivate-user


Deactivates a single user.

Base Command

okta-deactivate-user

Input

Argument Name Description Required
username Username to deactivate. Required

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-deactivate-user username=testForDocs@test.com

Human Readable Output

User testForDocs@test.com deactivated

okta-activate-user


Activates a single user.

Base Command

okta-activate-user

Input

Argument Name Description Required
username Username to activate. Required

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-activate-user username=testForDocs@test.com

Human Readable Output

testForDocs@test.com is active now

okta-suspend-user


Suspends a single user. This operation can only be performed on users with an ACTIVE status. After the porcess is completed, the user’s status is SUSPENDED.

Base Command

okta-suspend-user

Input

Argument Name Description Required
username Username to suspend. Required

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-suspend-user username=testForDocs@test.com

Human Readable Output

testForDocs@test.com status is Suspended

okta-unsuspend-user


Returns a single user to ACTIVE status. This operation can only be performed on users that have a SUSPENDED status.

Base Command

okta-unsuspend-user

Input

Argument Name Description Required
username Username to change the status to ACTIVE. Required

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-unsuspend-user username=testForDocs@test.com

Human Readable Output

testForDocs@test.com is no longer SUSPENDED

okta-get-user-factors


Returns all the enrolled factors for the specified user.

Base Command

okta-get-user-factors

Input

Argument Name Description Required
username Username for which to return all enrolled factors. Optional
userId User ID of the user for which to get all enrolled factors. Optional

Context Output

Path Type Description
Account.ID String Okta account ID.
Account.Factor.ID String Okta account factor ID.
Account.Factor.Provider String Okta account factor provider.
Account.Factor.Profile String Okta account factor profile.
Account.Factor.FactorType String Okta account factor type.
Account.Factor.Status Unknown Okta account factor status.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-user-factors username=factor@test.com

Context Example
{
    "Account": {
        "Factor": [
            {
                "FactorType": "sms",
                "ID": "mblpt21nffaaN5F060h7",
                "Profile": {
                    "phoneNumber": "+12025550191"
                },
                "Provider": "OKTA",
                "Status": "PENDING_ACTIVATION"
            },
            {
                "FactorType": "token:software:totp",
                "ID": "uftpt24kdrDJ7fDOq0h7",
                "Profile": {
                    "credentialId": "factor@test.com"
                },
                "Provider": "GOOGLE",
                "Status": "PENDING_ACTIVATION"
            },
            {
                "FactorType": "push",
                "ID": "opfpt1joeaArlg27g0h7",
                "Provider": "OKTA",
                "Status": "PENDING_ACTIVATION"
            }
        ],
        "ID": "00upt1w8tgFQM2v6t4"
    }
}
Human Readable Output

Factors for user: 00upt1w8tgFQM2v0h7

Factors

FactorType ID Profile Provider Status
sms mbgt21nffaaN5F060h7 phoneNumber: +12025550191 OKTA PENDING_ACTIVATION
token:software:totp uftptgdrDJ7fDOq0h7 credentialId: factor@test.com GOOGLE PENDING_ACTIVATION
push opfg1joeaArlg27g0h7   OKTA PENDING_ACTIVATION

okta-reset-factor


Un-enrolls an existing factor for the specified user. This enables the user to enroll a new factor.

Base Command

okta-reset-factor

Input

Argument Name Description Required
userId The user ID. Optional
username Username for which to un-enroll an existing factor. Optional
factorId The ID of the factor to reset. Required

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-reset-factor factorId=ufsq7cvptfbjQa72c0h7 userId=00upt1w8t40wFQM2v6t4

Human Readable Output

Factor: ufsq7cvptfbjQa72c0h7 deleted

okta-set-password


Sets passwords without validating existing user credentials.

Base Command

okta-set-password

Input

Argument Name Description Required
username Okta username for which to set the password. Required
password The new password to set for the user. Required
temporary_password When true, you’ll need to change the password in the next login. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-set-password username=testForDocs@test.com password=N3wPa55word!

Human Readable Output

testForDocs@test.com password was last changed on 2020-03-26T13:57:13.000Z

okta-add-to-group


Adds a user to a group with OKTA_GROUP type.

Base Command

okta-add-to-group

Input

Argument Name Description Required
userId ID of the user to add to the group. Optional
username Name of the user to add to the group. Optional
groupId ID of the group to add the user to. Optional
groupName Name of the group to add the user to. Optional

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-add-to-group groupName=Demisto username=testForDocs@test.com

Human Readable Output

User: 00uqk1qesl3k0SRbH0h7 added to group: Demisto successfully

okta-remove-from-group


Removes a user from a group with OKTA_GROUP type.

Base Command

okta-remove-from-group

Input

Argument Name Description Required
userId ID of the user to remove from the group. Optional
username Name of the user to remove from the group. Optional
groupId ID of the group to remove the user from. Optional
groupName Name of the group to remove the user from. Optional

Context Output

There is no context output for this command.

Command Example

!okta-remove-from-group groupName=demisto username=testForDocs@test.com

Human Readable Output

User: 00uqk1qesl3k0SRbH0h7 was removed from group: demisto successfully

okta-get-groups


Returns all user groups associated with a specified user.

Base Command

okta-get-groups

Input

Argument Name Description Required
username Username in Okta for which to get the associated groups. Required

Context Output

Path Type Description
Account.Group Unknown Okta groups with which the account is associated.
Account.ID String Okta account ID.
Account.Type String Okta account type.
Account.Group.ID String Unique key for the group.
Account.Group.Created Date Timestamp when the group was created.
Account.Group.ObjectClass String The object class, which determines the group’s profile.
Account.Group.LastUpdated Date Timestamp when the group’s profile was last updated.
Account.Group.LastMembershipUpdated Date Timestamp when the group’s memberships were last updated.
Account.Group.Type String Group type, which determines how a group’s profile and memberships are managed.
Account.Group.Description String Description of the group.
Account.Group.Name String Name of the group.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-groups username=testForDocs@test.com

Context Example
{
    "Account": {
        "Group": [
            {
                "Created": "2016-04-12T15:01:50.000Z",
                "Description": "All users in your organization",
                "ID": "00g66lckcsAJpLcNc0h7",
                "LastMembershipUpdated": "2020-03-26T13:56:49.000Z",
                "LastUpdated": "2016-04-12T15:01:50.000Z",
                "Name": "Everyone",
                "ObjectClass": [
                    "okta:user_group"
                ],
                "Type": "BUILT_IN"
            },
            {
                "Created": "2018-01-19T02:02:06.000Z",
                "ID": "00gdougcq3zEaf7c50h7",
                "LastMembershipUpdated": "2020-03-26T13:49:47.000Z",
                "LastUpdated": "2018-01-19T02:02:06.000Z",
                "Name": "Demisto",
                "ObjectClass": [
                    "okta:user_group"
                ],
                "Type": "OKTA_GROUP"
            }
        ],
        "ID": "00uqk1qesl3k0SRbH0h7",
        "Type": "Okta"
    }
}
Human Readable Output

Okta groups for user: testForDocs@test.com

Groups

Created Description ID LastMembershipUpdated LastUpdated Name ObjectClass Type
2016-04-12T15:01:50.000Z All users in your organization 00g66lckgAJpLcNc0h7 2020-03-26T13:56:49.000Z 2016-04-12T15:01:50.000Z Everyone okta:user_group BUILT_IN
2018-01-19T02:02:06.000Z   00gdougcgzEaf7c50h7 2020-03-26T13:49:47.000Z 2018-01-19T02:02:06.000Z Demisto okta:user_group OKTA_GROUP

okta-verify-push-factor


Enrolls and verifies a push factor for the specified user.

Base Command

okta-verify-push-factor

Input

Argument Name Description Required
userId The ID of the user to enroll and verify. Required
factorId The push factor ID. Required
polling_time Time to wait between subsequent polling calls. Value is in seconds. Default time is 5 seconds. Optional
max_polling_calls Maximum number of polling calls. Default value is 10. Optional
polling Whether to poll for the push factor challenge result. Possible values are: true, false. Default is true. Optional

Context Output

Path Type Description
Account.ID String Okta user ID.
Account.VerifyPushResult String Okta user push factor result.
Okta.PollingStatusURL String The polling URL for the push factor challenge.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-verify-push-factor factorId=opfpt1joeaArlg27g0h7 userId=00upt1w8t40wFQM2v0h7

Human Readable Output

Verify push factor result for user 00upt1w8t40wgQM2v0h7: WAITING

Context Example
{
    "factorResult": "WAITING",
    "profile": {
        "credentialId": "test@this.com",
        "deviceType": "SmartPhone_IPhone",
        "keys": [
            {
                "kty": "EC",
                "use": "sig",
                "kid": "default",
                "x": "3Y53lDoQYwzzVbjsbsPnqOnVaotIrVByQh5Sa-RwOHQ",
                "y": "0zHY_y9rVh-bq_-lR-MrmzNtUZrrIMbTrsjtxUyUT2Q",
                "crv": "P-256"
            }
        ],
        "name": "iPhone (5)",
        "platform": "IOS",
        "version": "13.1.3"
    },
    "expiresAt": "2020-02-24T11:37:08.000Z",
    "_links": {
        "cancel": {
            "href": "https://test.com/api/v1/users/00upt1w8t40wgQM2v0h7/factors/FactorID/transactions/TransactionID",
            "hints": {
                "allow": [
                    "DELETE"
                ]
            }
        },
        "poll": {
            "href": "https://test.com/api/v1/users/00upt1w8t40wgQM2v0h7/factors/FactorID/transactions/TransactionID",
            "hints": {
                "allow": [
                    "GET"
                ]
            }
        }
    }
}

okta-verify-mfa-status


Verifies the status of a push factor challenge.

Base Command

okta-verify-mfa-status

Input

Argument Name Description Required
polling_url The polling URL for the push factor challenge. Required

Context Output

Path Type Description
Okta.FactorResult.ID String The push factor challenge ID.
Okta.FactorResult.factorResult String The result of the factor challenge.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-verify-mfa-status polling_url="https://test.com/api/v1/users/TestID/factors/FactorID/transactions/TransactionID"

Human Readable Output

The status of the push factor is SUCCESS

okta-search


Searches for Okta users.

Base Command

okta-search

Input

Argument Name Description Required
term Term by which to search. Can be a first name, last name, or email address. The argument term or advanced_search is required. Optional
advanced_search Searches for users with a supported filtering expression for most properties, including custom-defined properties. The argument term or advanced_search is required. Optional
limit The maximum number of results to return. The default and maximum is 200. Optional
verbose Whether to return details of users that match the found term. Can be “true” or “false”. The default is “false”. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
Account.ID String Okta account IDs returned by the search.
Account.Username String Okta account usernames returned by the search.
Account.Email String Okta account emails returned by the search.
Account.DisplayName String Okta account display names returned by the search.
Account.Type String Okta account type returned by the search.
Account.Status String Okta account current status.
Account.Created Date Timestamp for when the user was created.
Account.Activated Date Timestamp for when the user was activated.
Account.StatusChanged Date Timestamp for when the user’s status was last changed.
Account.PasswordChanged Date Timestamp for when the user’s password was last changed.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-search term=test verbose=true

Context Example
{
    "Account": [
               {
            "Activated": "2020-02-12T14:03:51.000Z",
            "Created": "2020-02-12T14:03:50.000Z",
            "DisplayName": "bar test",
            "Email": "bartest@test.com",
            "ID": "00uppjeleqJQ2kkN80h7",
            "Status": "PROVISIONED",
            "StatusChanged": "2020-02-12T14:03:51.000Z",
            "Type": "Okta",
            "Username": "bartest@test.com"
        },
        {
            "Activated": "2020-02-19T12:33:20.000Z",
            "Created": "2018-07-31T12:48:33.000Z",
            "DisplayName": "test that",
            "Email": "test@that.com",
            "ID": "00ufufhqits3y78Ju0h7",
            "PasswordChanged": "2020-02-06T13:32:56.000Z",
            "Status": "PROVISIONED",
            "StatusChanged": "2020-02-19T12:33:20.000Z",
            "Type": "Okta",
            "Username": "test@that.com"
        },
        {
            "Activated": "2020-03-26T13:56:52.000Z",
            "Created": "2020-03-26T13:56:49.000Z",
            "DisplayName": "test that",
            "Email": "testForDocs@test.com",
            "ID": "00uqk1qesl3k0SRbH0h7",
            "PasswordChanged": "2020-03-26T13:56:50.000Z",
            "Status": "ACTIVE",
            "StatusChanged": "2020-03-26T13:56:52.000Z",
            "Type": "Okta",
            "Username": "testForDocs@test.com"
        }
    ]
}
Human Readable Output

Okta users found:
User:bartest@test.com

Profile

Email First Name Last Name Login Mobile Phone Second Email
bartest@test.com bar test bartest@test.com    
Additional Data
Activated Created Credentials ID Last Login Last Updated Password Changed Status Status Changed Type _links
2020-02-12T14:03:51.000Z 2020-02-12T14:03:50.000Z provider: {“type”: “OKTA”, “name”: “OKTA”} 00uppjeleqJQ2kkN80h7   2020-02-12T14:03:51.000Z   PROVISIONED   id: oty66lckcvDyVcGzS0h7 self: {“href”: “https://yourdomain.okta.com/api/v1/users/00uppjeleqJQ2kkN80h7”}
User:test@that.com
Profile
Email First Name Last Name Login Mobile Phone Second Email
test@that.com test that test@that.com   test@that.com
Additional Data
Activated Created Credentials ID Last Login Last Updated Password Changed Status Status Changed Type _links
2020-02-19T12:33:20.000Z 2018-07-31T12:48:33.000Z provider: {“type”: “OKTA”, “name”: “OKTA”} 00ufufhqits3y78Ju0h7   2020-02-19T12:33:20.000Z 2020-02-06T13:32:56.000Z PROVISIONED   id: oty66lckcvDyVcGzS0h7 self: {“href”: “https://yourdomain.okta.com/api/v1/users/00ufufhqits3y78Ju0h7”}
User:testForDocs@test.com
Profile
Email First Name Last Name Login Mobile Phone Second Email
testForDocs@test.com test that testForDocs@test.com    
Additional Data
Activated Created Credentials ID Last Login Last Updated Password Changed Status Status Changed Type _links
2020-03-26T13:56:52.000Z 2020-03-26T13:56:49.000Z password: {}recovery_question: {“question”: “whats is your favourite integration”}provider: {“type”: “OKTA”, “name”: “OKTA”} 00uqk1qesl3k0SRbH0h7   2020-03-26T13:56:52.000Z 2020-03-26T13:56:50.000Z ACTIVE   id: oty66lckcvDyVcGzS0h7 self: {“href”: “https://yourdomain.okta.com/api/v1/users/00uqk1qesl3k0SRbH0h7”}

okta-get-user


Fetches information for a single user. You must enter one or more parameters for the command to run.

Base Command

okta-get-user

Input

Argument Name Description Required
username Okta username for which to get information. Optional
userId User ID of the user for which to get information. Optional
userEmail Email of the user for which to get information. Optional
verbose Whether to return extended user information. Can be “true” or “false”. The default is “false”. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
Account.ID String Okta account ID.
Account.Email String Okta account email.
Account.Username String Okta account username.
Account.DisplayName String Okta account display name.
Account.Status String Okta account status.
Account.Created Date Timestamp for when the user was created.
Account.Activated Date Timestamp for when the user was activated.
Account.StatusChanged Date Timestamp for when the user’s status was last changed.
Account.PasswordChanged Date Timestamp for when the user’s password was last changed.
Account.Manager String The manager.
Account.ManagerEmail String The manager email.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-user username=testForDocs@test.com verbose=true

Context Example
{
    "Account": {
        "Activated": "2020-03-26T13:56:52.000Z",
        "Created": "2020-03-26T13:56:49.000Z",
        "DisplayName": "test that",
        "Email": "testForDocs@test.com",
        "ID": "00uqk1qesl3k0SRbH0h7",
        "Manager": "manager@test.com",
        "ManagerEmail": null,
        "PasswordChanged": "2020-03-26T13:56:50.000Z",
        "Status": "ACTIVE",
        "StatusChanged": "2020-03-26T13:56:52.000Z",
        "Type": "Okta",
        "Username": "testForDocs@test.com"
    }
}
Human Readable Output

User:testForDocs@test.com

Profile

Email First Name Last Name Login Manager Manager Email Mobile Phone Second Email
testForDocs@test.com test that testForDocs@test.com manager@test.com      
Additional Data
Activated Created Credentials ID Last Login Last Updated Password Changed Status Status Changed Type _links
2020-03-26T13:56:52.000Z 2020-03-26T13:56:49.000Z password: {}recovery_question: {“question”: “whats is your favourite integration”} provider: {“type”: “OKTA”, “name”: “OKTA”} 00uqk1qesl3k0SRbH0h7   2020-03-26T13:56:52.000Z 2020-03-26T13:56:50.000Z ACTIVE   id: oty66lckcvDyVcGzS0h7 links

okta-list-users


Lists users in your organization.

Base Command

okta-list-users

Input

Argument Name Description Required
after The cursor in which to retrive the results from and on. if the query didn’t reach the end of results, the tag can be obtained from the bottom of the grid in the readable output, or in the context path Okta.User.tag. Optional
limit The maximum number of results to return. Default is 200. Optional
verbose Whether to return extended user information. Possible values are: true, false. Default is false. Optional
query Searches the name property of groups for matching values. Optional
filter Useful for performing structured queries where constraints on group attribute values can be explicitly targeted.
The following expressions are supported(among others) for groups with the filter query parameter:
type eq “OKTA_GROUP” - Groups that have a type of OKTA_GROUP; lastUpdated lt “yyyy-MM-dd’‘T’‘HH:mm:ss.SSSZ” - Groups with profile last updated before a specific timestamp; lastMembershipUpdated eq “yyyy-MM-dd’‘T’‘HH:mm:ss.SSSZ” - Groups with memberships last updated at a specific timestamp; id eq “00g1emaKYZTWRYYRRTSK” - Group with a specified ID. For more information about filtering, visit https://developer.okta.com/docs/api/getting_started/design_principles#filtering.
Optional

Context Output

Path Type Description
Account.ID String Okta account ID.
Account.Email String Okta account email.
Account.Username String Okta account username.
Account.DisplayName String Okta account display name.
Account.Status String Okta account status.
Account.Created Date Timestamp for when the user was created.
Account.Activated Date Timestamp for when the user was activated.
Account.StatusChanged Date Timestamp for when the user’s status was last changed.
Account.PasswordChanged Date Timestamp for when the user’s password was last changed.
Okta.User.tag String The location of the next item, used with after param.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).

okta-create-user


Creates a new user with an option of setting a password, recovery question, and answer. The new user will immediately be able to log in after activation with the assigned password. This flow is common when developing a custom user registration experience.

Base Command

okta-create-user

Input

Argument Name Description Required
firstName First name of the user (givenName). Required
lastName Family name of the user (familyName). Required
email Primary email address of the user. Required
login Unique identifier for the user (username). Required
secondEmail Secondary email address of user. Usually used for account recovery. Optional
middleName Middle name(s) of the user. Optional
honorificPrefix A comma-separated list of honorific prefix(es) of the user, or title in most Western languages. Optional
honorificSuffix A comma-separated list of honorific suffix(es) of the user. Optional
title User’s title. for example, Vice President. Optional
displayName Display name of the user. Optional
nickName Casual way to address the user (nick name). Optional
profileUrl URL of the user online profile. For example, a web page. Optional
primaryPhone Primary phone number of the user. Optional
mobilePhone Mobile phone number of the user. Optional
streetAddress Full street address component of the user’s address. Optional
city City or locality component of the user’s address (locality). Optional
state State or region component of the user’s address (region). Optional
zipCode Zip code or postal code component of the user’s address (postalCode). Optional
countryCode Country name component of the user’s address (country). Optional
postalAddress Mailing address component of the user’s address. Optional
preferredLanguage User’s preferred written or spoken languages. Optional
locale User’s default location, for purposes of localizing items such as currency, date-time format, numerical representations, etc. Optional
timezone User’s time zone. Optional
userType The user type, which is used to identify the organization-to-user relationship such as “Employee” or “Contractor”. Optional
employeeNumber Organization or company assigned unique identifier for the user. Optional
costCenter Name of a cost center the user is assigned to. Optional
organization Name of the user’s organization. Optional
division Name of the user’s division. Optional
department Name of the user’s department. Optional
managerId ID of the user’s manager. Optional
manager Display name of the user’s manager. Optional
password Password for the new user. Optional
passwordQuestion Password question for the new user. Optional
passwordAnswer Password answer for question. Optional
providerType The provider type. Can be “OKTA”, “ACTIVE_DIRECTORY”, “LDAP”, “FEDERATION”, or “SOCIAL”. Possible values are: OKTA, ACTIVE_DIRECTORY, LDAP, FEDERATION, SOCIAL. Optional
providerName Name of the provider. Optional
groupIds IDs of groups that the user will be immediately added to at time of creation (does Not include default group). Optional
activate Whether to activate the lifecycle operation when creating the user. Can be “true” or “false”. Possible values are: true, false. Optional

Context Output

Path Type Description
Account.ID String Created Okta account ID.
Account.Email String Created Okta account email address.
Account.Username String Created okta account username.
Account.DisplayName String Created Okta account display name.
Account.Type String Type of created account - Okta.
Account.Status String Okta account current status.
Account.Created Date Timestamp for when the user was created.
Account.Activated Date Timestamp for when the user was activated.
Account.StatusChanged Date Timestamp for when the user’s status was last changed.
Account.PasswordChanged Date Timestamp for when the user’s password was last changed.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-create-user email=testForDocs@test.com firstName=test lastName=that login=testForDocs@test.com password=Pa55word! passwordQuestion="whats is your favourite integration" passwordAnswer="Okta of course"

Context Example
{
    "Account": {
        "Activated": null,
        "Created": "2020-03-26T13:56:49.000Z",
        "DisplayName": "test that",
        "Email": "testForDocs@test.com",
        "ID": "00uqk1qesl3k0SRbH0h7",
        "PasswordChanged": "2020-03-26T13:56:50.000Z",
        "Status": "STAGED",
        "StatusChanged": null,
        "Type": "Okta",
        "Username": "testForDocs@test.com"
    }
}
Human Readable Output

Okta User Created: testForDocs@test.com

First Name ID Last Login Last Name Login Mobile Phone Status
test 00uqk1qesl3k0SRbH0h7   that testForDocs@test.com   STAGED

okta-update-user


Updates a user with a given login. All fields are optional. Fields which are not set, will not be overwritten.

Base Command

okta-update-user

Input

Argument Name Description Required
firstName First name of the user (given name). Optional
lastName Family name of the user. Optional
email Primary email address of the user. Optional
username Unique identifier for the user (login). Required
secondEmail Secondary email address of the user (typically used for account recovery. Optional
middleName Middle name(s) of the user. Optional
honorificPrefix Honorific prefix(es) of the user, or title in most Western languages. Optional
honorificSuffix Honorific suffix(es) of the user. Optional
title User’s title. For example, Vice President. Optional
displayName Display name of the user. Optional
nickName Casual way to address the user in real life (nick name). Optional
profileUrl URL of the user’s online profile. For example, a web page. Optional
primaryPhone Primary phone number of the user. Optional
mobilePhone Mobile phone number of the user. Optional
streetAddress Full street address component of the user’s address. Optional
city City or locality component of the user’s address (locality). Optional
state State or region component of the user’s address (region). Optional
zipCode Zip code or postal code component of the user’s address (postalCode). Optional
countryCode Country name component of the user’s address (country). Optional
postalAddress Mailing address component of the user’s address. Optional
preferredLanguage User’s preferred written or spoken languages. Optional
locale User’s default location for purposes of localizing items such as currency, date-time format, numerical representations, etc. Optional
timezone User time zone. Optional
userType The user type, which is used to identify the organization-to-user relationship such as “Employee” or “Contractor”. Optional
employeeNumber Organization or company assigned unique identifier for the user. Optional
costCenter Name of a cost center the user is assigned to. Optional
organization Name of the user’s organization. Optional
division Name of the user’s division. Optional
department Name of the user’s department. Optional
managerId ID of the user’s manager. Optional
manager Display name of the user’s manager. Optional
password New password for the specified user. Optional
passwordQuestion Password question for the specified user. Optional
passwordAnswer Password answer for the question. Optional
providerType The provider type. Can be “OKTA”, “ACTIVE_DIRECTORY”, “LDAP”, “FEDERATION”, or “SOCIAL”. Possible values are: OKTA, ACTIVE_DIRECTORY, FEDERATION, SOCIAL. Optional
providerName Name of the provider. Optional

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-update-user username=testForDocs@test.com firstName="First Name Updated"

Human Readable Output

Okta user: testForDocs@test.com Updated

email firstName lastName login mobilePhone secondEmail
testForDocs@test.com First Name Updated that testForDocs@test.com    

okta-get-group-members


Enumerates all users that are members of a group.

Base Command

okta-get-group-members

Input

Argument Name Description Required
groupId ID of the group. Optional
limit The maximum number of results to return. Optional
verbose Whether to print extended user details. Can be “true” or “false”. The default is “false”. Possible values are: true, false. Default is false. Optional
groupName Name of the group. Optional

Context Output

Path Type Description
Account.ID String Okta account ID.
Account.Email String Okta account email address.
Account.Username String Okta account username.
Account.DisplayName String Okta account display name.
Account.Type String Account type - Okta.
Account.Status String Okta account current status.
Account.Created Date Timestamp for when the user was created.
Account.Activated Date Timestamp for when the user was activated.
Account.StatusChanged Date Timestamp for when the user’s status was last changed.
Account.PasswordChanged Date Timestamp for when the user’s password was last changed.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-group-members groupName=Demisto limit=1 verbose=true

Context Example
{
    "Account": {
        "Created": "2016-04-12T15:01:52.000Z",
        "DisplayName": "Test Demisto",
        "Email": "XSOAR@demisto.com",
        "ID": "00u66ljhpjidYi0h7",
        "PasswordChanged": "2020-02-24T11:40:08.000Z",
        "Status": "ACTIVE",
        "StatusChanged": "2016-04-12T15:05:06.000Z",
        "Type": "Okta",
        "Username": "XSOAR@demisto.com"
    }
}
Human Readable Output

Users for group: Demisto

User:Test@demisto.com

Profile

Email First Name Last Name Login Mobile Phone Second Email
XSOAR@demisto.com Test Demisto XSOAR@demisto.com    
Additional Data
Activated Created Credentials ID Last Login Last Updated Password Changed Status Status Changed Type _links
  2016-04-12T15:01:52.000Z password: {} recovery_question: {“question”: “born city”} provider: {“type”: “OKTA”, “name”: “OKTA”} 00u66lckd7lpjidYi0h7 2020-03-12T09:54:36.000Z 2020-02-24T11:42:22.000Z 2020-02-24T11:40:08.000Z ACTIVE   id: oty66lckcyVcGzS0h7 self: {“href”: “https://yourdomain.okta.com/api/v1/users/00uclpjidYi0h7”}

okta-list-groups


Lists groups in your organization. A subset of groups can be returned that match a supported filter expression or query.

Base Command

okta-list-groups

Input

Argument Name Description Required
query Searches the name property of groups for matching values. Optional
filter Useful for performing structured queries where constraints on group attribute values can be explicitly targeted.
The following expressions are supported(among others) for groups with the filter query parameter:
type eq “OKTA_GROUP” - Groups that have a type of OKTA_GROUP; lastUpdated lt “yyyy-MM-dd’‘T’‘HH:mm:ss.SSSZ” - Groups with profile last updated before a specific timestamp; lastMembershipUpdated eq “yyyy-MM-dd’‘T’‘HH:mm:ss.SSSZ” - Groups with memberships last updated at a specific timestamp; id eq “00g1emaKYZTWRYYRRTSK” - Group with a specified ID. For more information about filtering, visit https://developer.okta.com/docs/api/getting_started/design_principles#filtering.
Optional
limit The maximum number of results to return. The default is 200. Default is 200. Optional

Context Output

Path Type Description
Okta.Group.ID String Unique key for the group.
Okta.Group.Created Date Timestamp for when the group was created.
Okta.Group.ObjectClass Unknown The group’s profile.
Okta.Group.LastUpdated Date Timestamp for when the group’s profile was last updated.
Okta.Group.LastMembershipUpdated Date Timestamp for when the group’s membership was last updated.
Okta.Group.Type String The group type, which determines how a group’s profile and membership are managed. Can be “OKTA_GROUP”, “APP_GROUP”, or “BUILT_IN”.
Okta.Group.Name String Name of the group.
Okta.Group.Description String Description of the group.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-list-groups filter=`type eq "OKTA_GROUP" and lastUpdated lt "2019-04-30T00:00:00.000Z" and lastMembershipUpdated gt "2019-04-30T00:00:00.000Z"` query=demisto

Context Example
{
    "Okta": {
        "Group": {
            "Created": "2018-01-19T02:02:06.000Z",
            "ID": "00gdout3zEaf7c50h7",
            "LastMembershipUpdated": "2020-03-26T13:56:56.000Z",
            "LastUpdated": "2018-01-19T02:02:06.000Z",
            "Name": "Demisto",
            "ObjectClass": [
                "okta:user_group"
            ],
            "Type": "OKTA_GROUP"
        }
    }
}
Human Readable Output

Groups

Created Description ID LastMembershipUpdated LastUpdated Name ObjectClass Type
2018-01-19T02:02:06.000Z   00gdougctEaf7c50h7 2020-03-26T13:56:56.000Z 2018-01-19T02:02:06.000Z Demisto okta:user_group OKTA_GROUP

okta-get-failed-logins


Returns failed login events.

Base Command

okta-get-failed-logins

Input

Argument Name Description Required
since Filters the lower time bound of the log events in the Internet Date/Time Format profile of ISO 8601. An example: 2017-05-03T16:22:18Z. Optional
until Filters the upper time bound of the log events in the Internet Date/Time Format profile of ISO 8601. An example: 2017-05-03T16:22:18Z. Optional
sortOrder The order of the returned events. Can be “ASCENDING” or “DESCENDING”. The default is “ASCENDING”. Possible values are: ASCENDING, DESCENDING. Default is ASCENDING. Optional
limit The maximum number of results to return. The default is 100. Optional

Context Output

Path Type Description
Okta.Logs.Events.actor.alternateId String Alternative ID of the actor.
Okta.Logs.Events.actor.displayName String Display name of the actor.
Okta.Logs.Events.actor.id String ID of the actor.
Okta.Logs.Events.client.userAgent.rawUserAgent String A raw string representation of the user agent, formatted according to section 5.5.3 of HTTP/1.1 Semantics and Content. Both the browser and the OS fields can be derived from this field.
Okta.Logs.Events.client.userAgent.os String The operating system on which the client runs. For example, Microsoft Windows 10.
Okta.Logs.Events.client.userAgent.browser String Identifies the browser type, if relevant. For example, Chrome.
Okta.Logs.Events.client.device String Type of device that client operated from. For example, Computer.
Okta.Logs.Events.client.id String For OAuth requests, the ID of the OAuth client making the request. For SSWS token requests, the ID of the agent making the request.
Okta.Logs.Events.client.ipAddress String IP address from which the client made its request.
Okta.Logs.Events.client.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.client.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example Montana, Incheon.
Okta.Logs.Events.client.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.displayMessage String The display message for an event.
Okta.Logs.Events.eventType String Type of event that was published.
Okta.Logs.Events.outcome.result String Result of the action. Can be “SUCCESS”, “FAILURE”, “SKIPPED”, “UNKNOWN”.
Okta.Logs.Events.outcome.reason String Reason for the result. For example, INVALID_CREDENTIALS.
Okta.Logs.Events.published String Timestamp when the event was published.
Okta.Logs.Events.severity String The event severity. Can be “DEBUG”, “INFO”, “WARN”, or “ERROR”.
Okta.Logs.Events.securityContext.asNumber Number Autonomous system number associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.asOrg String Organization associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.isp String Internet service provider used to send the event’s request.
Okta.Logs.Events.securityContext.domain String The domain name associated with the IP address of the inbound event request.
Okta.Logs.Events.securityContext.isProxy String Specifies whether an event’s request is from a known proxy.
Okta.Logs.Events.request.ipChain.IP String IP address.
Okta.Logs.Events.request.ipChain.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.request.ipChain.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.request.ipChain.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.request.ipChain.source String Details regarding the source.
Okta.Logs.Events.target.id String ID of a target.
Okta.Logs.Events.target.type String Type of a target.
Okta.Logs.Events.target.alternateId String Alternative ID of a target.
Okta.Logs.Events.target.displayName String Display name of a target.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-failed-logins since="2019-04-30T00:00:00.000Z" limit=1

Context Example
{
    "Okta": {
        "Logs": {
            "Events": {
                "actor": {
                    "alternateId": "goo@test.com",
                    "detailEntry": null,
                    "displayName": "unknown",
                    "id": "unknown",
                    "type": "User"
                },
                "authenticationContext": {
                    "authenticationProvider": null,
                    "authenticationStep": 0,
                    "credentialProvider": null,
                    "credentialType": null,
                    "externalSessionId": "unknown",
                    "interface": null,
                    "issuer": null
                },
                "client": {
                    "device": "Computer",
                    "geographicalContext": {
                        "city": "Tel Aviv",
                        "country": "Israel",
                        "geolocation": {
                            "lat": 32.0678,
                            "lon": 34.7647
                        },
                        "postalCode": null,
                        "state": "Tel Aviv"
                    },
                    "id": null,
                    "ipAddress": "127.0.0.1",
                    "userAgent": {
                        "browser": "CHROME",
                        "os": "Windows 10",
                        "rawUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
                    },
                    "zone": "null"
                },
                "debugContext": {
                    "debugData": {
                        "loginResult": "VERIFICATION_ERROR",
                        "requestId": "XYM92q-0Fs0ewvRQZVAlzwAABL8",
                        "requestUri": "/api/v1/authn",
                        "url": "/api/v1/authn?"
                    }
                },
                "displayMessage": "User login to Okta",
                "eventType": "user.session.start",
                "legacyEventType": "core.user_auth.login_failed",
                "outcome": {
                    "reason": "VERIFICATION_ERROR",
                    "result": "FAILURE"
                },
                "published": "2019-09-19T08:35:38.353Z",
                "request": {
                    "ipChain": [
                        {
                            "geographicalContext": {
                                "city": "Tel Aviv",
                                "country": "Israel",
                                "geolocation": {
                                    "lat": 32.0678,
                                    "lon": 34.7647
                                },
                                "postalCode": null,
                                "state": "Tel Aviv"
                            },
                            "ip": "127.0.0.1",
                            "source": null,
                            "version": "V4"
                        }
                    ]
                },
                "securityContext": {
                    "asNumber": null,
                    "asOrg": null,
                    "domain": null,
                    "isProxy": null,
                    "isp": null
                },
                "severity": "INFO",
                "target": null,
                "transaction": {
                    "detail": {},
                    "id": "XYM92q-0Fs0ewvRQZVAlzwAABL8",
                    "type": "WEB"
                },
                "uuid": "7503c6b4-dab8-11e9-b336-d163e95fbe00",
                "version": "0"
            }
        }
    }
}
Human Readable Output

Failed Login Events

Actor ActorAlternaneId ChainIP Client EventInfo EventOutcome EventSeverity RequestIP Targets Time
unknown (User) admin 127.0.0.1 CHROME on Windows 10 Computer User login to Okta FAILURE: VERIFICATION_ERROR INFO 127.0.0.1 - 09/30/2019, 18:42:38

okta-get-logs


Gets logs by providing optional filters.

Base Command

okta-get-logs

Input

Argument Name Description Required
filter Useful for performing structured queries where constraints on LogEvent attribute values can be explicitly targeted.
The following expressions are supported for events with the filter query parameter: eventType eq “ :eventType”
-Events that have a specific action; eventType target.id eq “:id”
- Events published with a specific target id; actor.id eq “:id”
- Events published with a specific actor ID. For more information about filtering, visit https://developer.okta.com/docs/api/getting_started/design_principles#filtering.
Optional
query The query parameter can be used to perform keyword matching against a LogEvents object’s attribute values. To satisfy the constraint, all supplied keywords must be matched exactly. Note that matching is case-insensitive. The following are some examples of common keyword filtering:
Events that mention a specific city: query=San Francisco;
Events that mention a specific url: query=interestingURI.com;
Events that mention a specific person: query=firstName lastName.
Optional
since Filters the lower time bound of the log events in the Internet Date/Time Format profile of ISO 8601. For example: 2017-05-03T16:22:18Z. Optional
until Filters the upper time bound of the log events in the Internet Date/Time Format profile of ISO 8601. For example: 2017-05-03T16:22:18Z. Optional
sortOrder The order of the returned events. Can be “ASCENDING” or “DESCENDING”. The default is “ASCENDING”. Possible values are: ASCENDING, DESCENDING. Default is ASCENDING. Optional
limit The maximum number of results to return. The default is 100. Default is 100. Optional

Context Output

Path Type Description
Okta.Logs.Events.actor.alternateId String Alternative ID of the actor.
Okta.Logs.Events.actor.displayName String Display name of the actor.
Okta.Logs.Events.actor.id String ID of the actor.
Okta.Logs.Events.client.userAgent.rawUserAgent String A raw string representation of user agent, formatted according to section 5.5.3 of HTTP/1.1 Semantics and Content. Both the browser and the OS fields can be derived from this field.
Okta.Logs.Events.client.userAgent.os String The operating system on which the client runs. For example, Microsoft Windows 10.
Okta.Logs.Events.client.userAgent.browser String Identifies the type of web browser, if relevant. For example, Chrome.
Okta.Logs.Events.client.device String Type of device from which the client operated. For example, Computer.
Okta.Logs.Events.client.id String For OAuth requests, the ID of the OAuth client making the request. For SSWS token requests, the ID of the agent making the request.
Okta.Logs.Events.client.ipAddress String IP address from which the client made its request.
Okta.Logs.Events.client.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.client.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.client.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.displayMessage String The display message for an event.
Okta.Logs.Events.eventType String Type of event that was published.
Okta.Logs.Events.outcome.result String Result of the action. Can be “SUCCESS”, “FAILURE”, “SKIPPED”, or “UNKNOWN”.
Okta.Logs.Events.outcome.reason String Reason for the result. For example, INVALID_CREDENTIALS.
Okta.Logs.Events.published String Timestamp when the event was published.
Okta.Logs.Events.severity String The event severity. Can be “DEBUG”, “INFO”, “WARN”, or “ERROR”.
Okta.Logs.Events.securityContext.asNumber Number Autonomous system number associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.asOrg String Organization associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.isp String Internet service provider used to send the event’s request.
Okta.Logs.Events.securityContext.domain String The domain name associated with the IP address of the inbound event request.
Okta.Logs.Events.securityContext.isProxy String Specifies whether an event’s request is from a known proxy.
Okta.Logs.Events.request.ipChain.IP String IP address.
Okta.Logs.Events.request.ipChain.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.request.ipChain.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.request.ipChain.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.request.ipChain.source String Details regarding the source.
Okta.Logs.Events.target.id String ID of a target.
Okta.Logs.Events.target.type String Type of a target.
Okta.Logs.Events.target.alternateId String Alternative ID of a target.
Okta.Logs.Events.target.displayName String Display name of a target.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-logs filter=`actor.id eq "00u66lckvpjidYi0h7"` query=Boardman since="2020-03-03T20:23:17.573Z" limit=1

Context Example
{
    "Okta": {
        "Logs": {
            "Events": {
                "actor": {
                    "alternateId": "Test@demisto.com",
                    "detailEntry": null,
                    "displayName": "Test Demisto",
                    "id": "00u66lvd7lpjidYi0h7",
                    "type": "User"
                },
                "authenticationContext": {
                    "authenticationProvider": null,
                    "authenticationStep": 0,
                    "credentialProvider": null,
                    "credentialType": null,
                    "externalSessionId": "trs3hs_F_UQT9K5FOPG7m4i1g",
                    "interface": null,
                    "issuer": null
                },
                "client": {
                    "device": "Unknown",
                    "geographicalContext": {
                        "city": "Boardman",
                        "country": "United States",
                        "geolocation": {
                            "lat": 45.8491,
                            "lon": -119.7143
                        },
                        "postalCode": "97818",
                        "state": "Oregon"
                    },
                    "id": null,
                    "ipAddress": "127.0.0.1",
                    "userAgent": {
                        "browser": "UNKNOWN",
                        "os": "Unknown",
                        "rawUserAgent": "Demisto/1.0"
                    },
                    "zone": "null"
                },
                "debugContext": {
                    "debugData": {
                        "requestId": "Xl68tch@7iZvNo0k8vPc5gAAAmE",
                        "requestUri": "/api/v1/groups/00g8mo0l5wuTxmoIC0h7/users/00uptu0jj9V91p5QM0h7",
                        "threatSuspected": "false",
                        "url": "/api/v1/groups/00g8mo0l5wuTxmoIC0h7/users/00uptu0jj9V91p5QM0h7?"
                    }
                },
                "displayMessage": "Remove user from group membership",
                "eventType": "group.user_membership.remove",
                "legacyEventType": "core.user_group_member.user_remove",
                "outcome": {
                    "reason": null,
                    "result": "SUCCESS"
                },
                "published": "2020-03-03T20:23:17.573Z",
                "request": {
                    "ipChain": [
                        {
                            "geographicalContext": {
                                "city": "Boardman",
                                "country": "United States",
                                "geolocation": {
                                    "lat": 45.8491,
                                    "lon": -119.7143
                                },
                                "postalCode": "97818",
                                "state": "Oregon"
                            },
                            "ip": "127.0.0.1",
                            "source": null,
                            "version": "V4"
                        }
                    ]
                },
                "securityContext": {
                    "asNumber": null,
                    "asOrg": null,
                    "domain": null,
                    "isProxy": null,
                    "isp": null
                },
                "severity": "INFO",
                "target": [
                    {
                        "alternateId": "test@this.com",
                        "detailEntry": null,
                        "displayName": "test this",
                        "id": "00uptu0jj9V91p5QM0h7",
                        "type": "User"
                    },
                    {
                        "alternateId": "unknown",
                        "detailEntry": null,
                        "displayName": "test1",
                        "id": "00g8mo0l5wuTxmoIC0h7",
                        "type": "UserGroup"
                    }
                ],
                "transaction": {
                    "detail": {},
                    "id": "Xl68tch@7iZvNo0k8vPc5gAAAmE",
                    "type": "WEB"
                },
                "uuid": "d14117f9-5d8c-11ea-a9cb-1f2fbd3b03f7",
                "version": "0"
            }
        }
    }
}
Human Readable Output

Okta Events

Actor ActorAlternaneId ChainIP Client EventInfo EventOutcome EventSeverity RequestIP Targets Time
Test Demisto (User) Test@demisto.com 127.0.0.1 Unknown browser on Unknown OS Unknown device Remove user from group membership SUCCESS INFO 127.0.0.1 test this (User) test1 (UserGroup) 03/03/2020, 20:23:17

okta-get-group-assignments


Gets events for when a user was added to a group.

Base Command

okta-get-group-assignments

Input

Argument Name Description Required
since Filters the lower time bound of the log event in the Internet Date\Time format profile of ISO 8601. For example, 2020-02-14T16:00:18Z. Optional
until Filters the upper time bound of the log event in the Internet Date\Time format profile of ISO 8601. For example, 2020-02-14T16:00:18Z. Optional
sortOrder The order of the returned events. Can be “ASCENDING” or “DESCENDING”. The default is “ASCENDING”. Possible values are: ASCENDING, DESCENDING. Default is ASCENDING. Optional
limit The maximum number of results to return. The default is 100. Default is 100. Optional

Context Output

Path Type Description
Okta.Logs.Events.actor.alternateId String Alternative ID of the actor.
Okta.Logs.Events.actor.displayName String Display name of the actor.
Okta.Logs.Event.actor.id String ID of the actor.
Okta.Logs.Events.client.userAgent.rawUserAgent String A raw string representation of user agent, formatted according to section 5.5.3 of HTTP/1.1 Semantics and Content. Both the browser and the OS fields can be derived from this field.
Okta.Logs.Events.client.userAgent.os String The operating system on which the client runs. For example, Microsoft Windows 10.
Okta.Logs.Events.client.userAgent.browser String Identifies the type of web browser, if relevant. For example, Chrome.
Okta.Logs.Events.client.device String Type of device from which the client operated. For example, Computer.
Okta.Logs.Events.client.id String For OAuth requests, the ID of the OAuth client making the request. For SSWS token requests, the ID of the agent making the request.
Okta.Logs.Events.client.ipAddress String IP address from which the client made its request.
Okta.Logs.Events.client.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.client.geographicalContext.state String Full name of the state or province encompassing in the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.client.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.displayMessage String The display message for an event.
Okta.Logs.Events.eventType String Type of event that was published.
Okta.Logs.Events.outcome.result String Result of the action. Can be “SUCCESS”, “FAILURE”, “SKIPPED”, or “UNKNOWN”.
Okta.Logs.Events.outcome.reason Unknown Reason for the result. For example INVALID_CREDENTIALS.
Okta.Logs.Events.published String Timestamp when the event was published.
Okta.Logs.Events.severity String The event severity. Can be “DEBUG”, “INFO”, “WARN”, or “ERROR”.
Okta.Logs.Events.securityContext.asNumber Number Autonomous system number associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.asOrg String Organization associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.isp String Internet service provider used to send the event’s request.
Okta.Logs.Events.securityContext.domain String The domain name associated with the IP address of the inbound event request.
Okta.Logs.Events.securityContext.isProxy String Specifies whether an event’s request is from a known proxy.
Okta.Logs.Events.request.ipChain.IP String IP address.
Okta.Logs.Events.request.ipChain.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.request.ipChain.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.request.ipChain.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.request.ipChain.source String Details regarding the source.
Okta.Logs.Events.target.id String ID of a target.
Okta.Logs.Events.target.type String Target type.
Okta.Logs.Events.target.alternateId String Alternative ID of a target.
Okta.Logs.Events.target.displayName String Display name of a target.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-group-assignments since="2019-04-30T00:00:00.000Z" limit=1

Context Example
{
    "Okta": {
        "Logs": {
            "Events": {
                "actor": {
                    "alternateId": "Test@demisto.com",
                    "detailEntry": null,
                    "displayName": "Test Demisto",
                    "id": "00u66lckd7lpjidYi0h7",
                    "type": "User"
                },
                "authenticationContext": {
                    "authenticationProvider": null,
                    "authenticationStep": 0,
                    "credentialProvider": null,
                    "credentialType": null,
                    "externalSessionId": "trs4IvlVrvVR9G8RPsPtFjwBA",
                    "interface": null,
                    "issuer": null
                },
                "client": {
                    "device": "Unknown",
                    "geographicalContext": {
                        "city": "Boardman",
                        "country": "United States",
                        "geolocation": {
                            "lat": 45.8491,
                            "lon": -119.7143
                        },
                        "postalCode": "97818",
                        "state": "Oregon"
                    },
                    "id": null,
                    "ipAddress": "127.0.0.1",
                    "userAgent": {
                        "browser": "UNKNOWN",
                        "os": "Unknown",
                        "rawUserAgent": "Demisto/1.0"
                    },
                    "zone": "null"
                },
                "debugContext": {
                    "debugData": {
                        "requestId": "XXxTqUauHPkBXo4-TEcw9QAAAq0",
                        "requestUri": "/api/v1/groups/00g8mo0l5wuTxmoIC0h7/users/00ued6gq9jItNhAsN0h7",
                        "url": "/api/v1/groups/00g8mo0l5wuTxmoIC0h7/users/00ued6gq9jItNhAsN0h7?"
                    }
                },
                "displayMessage": "Add user to group membership",
                "eventType": "group.user_membership.add",
                "legacyEventType": "core.user_group_member.user_add",
                "outcome": {
                    "reason": null,
                    "result": "SUCCESS"
                },
                "published": "2019-09-14T02:42:49.379Z",
                "request": {
                    "ipChain": [
                        {
                            "geographicalContext": {
                                "city": "Boardman",
                                "country": "United States",
                                "geolocation": {
                                    "lat": 45.8491,
                                    "lon": -119.7143
                                },
                                "postalCode": "97818",
                                "state": "Oregon"
                            },
                            "ip": "127.0.0.1",
                            "source": null,
                            "version": "V4"
                        }
                    ]
                },
                "securityContext": {
                    "asNumber": null,
                    "asOrg": null,
                    "domain": null,
                    "isProxy": null,
                    "isp": null
                },
                "severity": "INFO",
                "target": [
                    {
                        "alternateId": "test@this.com",
                        "detailEntry": null,
                        "displayName": "test this",
                        "id": "00ued6gq9jItNhAsN0h7",
                        "type": "User"
                    },
                    {
                        "alternateId": "unknown",
                        "detailEntry": null,
                        "displayName": "test1",
                        "id": "00g8mo0l5wuTxmoIC0h7",
                        "type": "UserGroup"
                    }
                ],
                "transaction": {
                    "detail": {},
                    "id": "XXxTqUauHPkBXo4-TEcw9QAAAq0",
                    "type": "WEB"
                },
                "uuid": "5741ef53-d699-11e9-a08c-d549acc8afb2",
                "version": "0"
            }
        }
    }
}
Human Readable Output

Group Assignment Events

Actor ActorAlternaneId ChainIP Client EventInfo EventOutcome EventSeverity RequestIP Targets Time
Test Demisto (User) Test@demisto.com 127.0.0.1 Unknown browser on Unknown OS Unknown device Add user to group membership SUCCESS INFO 127.0.0.1 test this (User) test1 (UserGroup) 09/29/2019, 03:47:46

okta-get-application-assignments


Returns events for when a user was assigned to an application.

Base Command

okta-get-application-assignments

Input

Argument Name Description Required
since Filters the lower time bound of the log event in the Internet Date\Time format profile of ISO 8601. For example, 2020-02-14T16:00:18Z. Optional
until Filters the upper time bound of the log event in the Internet Date\Time format profile of ISO 8601. For example, 2020-02-14T16:00:18Z. Optional
sortOrder The order of the returned events. Can be “ASCENDING” or “DESCENDING”. The default is “ASCENDING”. Possible values are: ASCENDING, DESCENDING. Default is ASCENDING. Optional
limit The maximum number of results to return. The default is 100. Default is 100. Optional

Context Output

Path Type Description
Okta.Logs.Events.actor.alternateId String Alternative ID of the actor.
Okta.Logs.Events.actor.displayName String Display name of the actor.
Okta.Logs.Event.actor.id String ID of the actor.
Okta.Logs.Events.client.userAgent.rawUserAgent String A raw string representation of the user agent, formatted according to section 5.5.3 of HTTP/1.1 Semantics and Content. Both the browser and the OS fields can be derived from this field.
Okta.Logs.Events.client.userAgent.os String The OS on which the client runs. For example, Microsoft Windows 10.
Okta.Logs.Events.client.userAgent.browser String Identifies the type of web browser, if relevant. For example, Chrome.
Okta.Logs.Events.client.device String Type of device from which the client operated. For example, Computer.
Okta.Logs.Events.client.id String For OAuth requests, the ID of the OAuth client making the request. For SSWS token requests, the ID of the agent making the request.
Okta.Logs.Events.client.ipAddress String IP address from which the client made its request.
Okta.Logs.Events.client.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.client.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.client.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.displayMessage String The display message for an event.
Okta.Logs.Events.eventType String Type of event that was published.
Okta.Logs.Events.outcome.result String Result of the action. For example, “SUCCESS”, “FAILURE”, “SKIPPED”, or “UNKNOWN”.
Okta.Logs.Events.outcome.reason String Reason for the result. For example INVALID_CREDENTIALS.
Okta.Logs.Events.published String Timestamp when the event was published.
Okta.Logs.Events.severity String The event severity. Can be “DEBUG”, “INFO”, “WARN”, or “ERROR”.
Okta.Logs.Events.securityContext.asNumber Number Autonomous system number associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.asOrg String Organization associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.isp String Internet service provider used to send the event’s request.
Okta.Logs.Events.securityContext.domain String The domain name associated with the IP address of the inbound event request.
Okta.Logs.Events.securityContext.isProxy String Specifies whether an event’s request is from a known proxy.
Okta.Logs.Events.request.ipChain.IP String IP address.
Okta.Logs.Events.request.ipChain.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.request.ipChain.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.request.ipChain.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.request.ipChain.source String Details regarding the source.
Okta.Logs.Events.target.id String ID of a target.
Okta.Logs.Events.target.type String Type of a target.
Okta.Logs.Events.target.alternateId String Alternative ID of a target.
Okta.Logs.Events.target.displayName String Display name of a target.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-application-assignments since="2019-04-30T00:00:00.000Z" until="2020-02-30T00:00:00.000Z" sortOrder=DESCENDING limit=1

Context Example
{
    "Okta": {
        "Logs": {
            "Events": {
                "actor": {
                    "alternateId": "Test@demisto.com",
                    "detailEntry": null,
                    "displayName": "Test Demisto",
                    "id": "00u66lckd7lpjidYi0h7",
                    "type": "User"
                },
                "authenticationContext": {
                    "authenticationProvider": null,
                    "authenticationStep": 0,
                    "credentialProvider": null,
                    "credentialType": null,
                    "externalSessionId": "trsFSV6XXY4TMCSB_xzJrZ85A",
                    "interface": null,
                    "issuer": null
                },
                "client": {
                    "device": "Unknown",
                    "geographicalContext": {
                        "city": "Boardman",
                        "country": "United States",
                        "geolocation": {
                            "lat": 45.8491,
                            "lon": -119.7143
                        },
                        "postalCode": "97818",
                        "state": "Oregon"
                    },
                    "id": null,
                    "ipAddress": "127.0.0.1",
                    "userAgent": {
                        "browser": "UNKNOWN",
                        "os": "Unknown",
                        "rawUserAgent": "python-requests/2.22.0"
                    },
                    "zone": "null"
                },
                "debugContext": {
                    "debugData": {
                        "requestId": "XlgCgAEBMJsNo5Yh9rHtZAAACPw",
                        "requestUri": "/api/v1/users/00upywm7l0rL1V0zt0h7/lifecycle/activate",
                        "threatSuspected": "false",
                        "url": "/api/v1/users/00upywm7l0rL1V0zt0h7/lifecycle/activate?"
                    }
                },
                "displayMessage": "Add user to application membership",
                "eventType": "application.user_membership.add",
                "legacyEventType": "app.generic.provision.assign_user_to_app",
                "outcome": {
                    "reason": null,
                    "result": "SUCCESS"
                },
                "published": "2020-02-27T17:55:12.949Z",
                "request": {
                    "ipChain": [
                        {
                            "geographicalContext": {
                                "city": "Boardman",
                                "country": "United States",
                                "geolocation": {
                                    "lat": 45.8491,
                                    "lon": -119.7143
                                },
                                "postalCode": "97818",
                                "state": "Oregon"
                            },
                            "ip": "127.0.0.1",
                            "source": null,
                            "version": "V4"
                        }
                    ]
                },
                "securityContext": {
                    "asNumber": null,
                    "asOrg": null,
                    "domain": null,
                    "isProxy": null,
                    "isp": null
                },
                "severity": "INFO",
                "target": [
                    {
                        "alternateId": "Test1@test.com",
                        "detailEntry": null,
                        "displayName": "Test 1 that",
                        "id": "0uapywj3yxcjhpjSQ0h7",
                        "type": "AppUser"
                    },
                    {
                        "alternateId": "ShrikSAML",
                        "detailEntry": null,
                        "displayName": "ShrikSAML",
                        "id": "0oabe0e2jruaQccDf0h7",
                        "type": "AppInstance"
                    },
                    {
                        "alternateId": "Test1@test.com",
                        "detailEntry": null,
                        "displayName": "Test 1 that",
                        "id": "00upywm7l0rL1V0zt0h7",
                        "type": "User"
                    }
                ],
                "transaction": {
                    "detail": {},
                    "id": "XlgCgAEBMJsNo5Yh9rHtZAAACPw",
                    "type": "WEB"
                },
                "uuid": "4d8a4e9a-598a-11ea-a594-b9fb637659a5",
                "version": "0"
            }
        }
    }
}
Human Readable Output

Application Assignment Events

Actor ActorAlternaneId ChainIP Client EventInfo EventOutcome EventSeverity RequestIP Targets Time
Test Demisto (User) Test@demisto.com 127.0.0.1 Unknown browser on Unknown OS Unknown device Add user to application membership SUCCESS INFO 127.0.0.1 Test 1 that (AppUser) ShrikSAML (AppInstance) Test 1 that (User) 02/27/2020, 17:55:12

okta-get-application-authentication


Returns logs using specified filters.

Base Command

okta-get-application-authentication

Input

Argument Name Description Required
since Filters the lower time bound of the log event in the Internet Date\Time format profile of ISO 8601. For example, 2020-02-14T16:00:18Z. Optional
until Filters the upper time bound of the log event in the Internet Date\Time format profile of ISO 8601. For example, 2020-02-14T16:00:18Z. Optional
sortOrder The order of the returned events. Can be “ASCENDING” or “DESCENDING”. The default is “ASCENDING”. Possible values are: ASCENDING, DESCENDING. Default is ASCENDING. Optional
limit The maximum number of results to return. The default is 100. Default is 100. Optional

Context Output

Path Type Description
Okta.Logs.Events.actor.alternateId String Alternative ID of the actor.
Okta.Logs.Events.actor.displayName String Display name of the actor.
Okta.Logs.Events.actor.id String ID of the actor.
Okta.Logs.Events.client.userAgent.rawUserAgent String A raw string representation of user agent, formatted according to section 5.5.3 of HTTP/1.1 Semantics and Content. Both the browser and the OS fields can be derived from this field.
Okta.Logs.Events.client.userAgent.os String The operating system on which the client runs. For example, Microsoft Windows 10.
Okta.Logs.Events.client.userAgent.browser String Identifies the type of web browser, if relevant. For example, Chrome.
Okta.Logs.Events.client.device String Type of device from which the client operated. For example, Computer.
Okta.Logs.Events.client.id String For OAuth requests, the ID of the OAuth client making the request. For SSWS token requests, the ID of the agent making the request.
Okta.Logs.Events.client.ipAddress String IP address from which the client made its request.
Okta.Logs.Events.client.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.client.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.client.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.displayMessage String The display message for an event.
Okta.Logs.Events.eventType String Type of event that was published.
Okta.Logs.Events.outcome.result String Result of the action. Can be “SUCCESS”, “FAILURE”, “SKIPPED”, or “UNKNOWN”.
Okta.Logs.Events.outcome.reason String Reason for the result. For example INVALID_CREDENTIALS.
Okta.Logs.Events.published String Timestamp when the event was published.
Okta.Logs.Events.severity String The event severity. Can be “DEBUG”, “INFO”, “WARN”, or “ERROR”.
Okta.Logs.Events.securityContext.asNumber Number Autonomous system number associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.asOrg String Organization associated with the autonomous system that the event request was sourced to.
Okta.Logs.Events.securityContext.isp String Internet service provider used to send the event’s request.
Okta.Logs.Events.securityContext.domain String The domain name associated with the IP address of the inbound event request.
Okta.Logs.Events.securityContext.isProxy String Specifies whether an event’s request is from a known proxy.
Okta.Logs.Events.request.ipChain.IP String IP address.
Okta.Logs.Events.request.ipChain.geographicalContext.city String The city encompassing the area containing the geo-location coordinates, if available. For example, Seattle, San Francisco.
Okta.Logs.Events.request.ipChain.geographicalContext.state String Full name of the state or province encompassing the area containing the geo-location coordinates. For example, Montana, Incheon.
Okta.Logs.Events.request.ipChain.geographicalContext.country String Full name of the country encompassing the area containing the geo-location coordinates. For example, France, Uganda.
Okta.Logs.Events.request.ipChain.source String Details regarding the source.
Okta.Logs.Events.target.id String ID of a target.
Okta.Logs.Events.target.type String Type of a target.
Okta.Logs.Events.target.alternateId String Alternative ID of a target.
Okta.Logs.Events.target.displayName String Display name of a target.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-get-application-authentication since="2019-04-30T00:00:00.000Z" until="2020-02-30T00:00:00.000Z" limit=1

Context Example
{
    "Okta": {
        "Logs": {
            "Events": {
                "actor": {
                    "alternateId": "Test@demisto.com",
                    "detailEntry": null,
                    "displayName": "Test Demisto",
                    "id": "00u66lckd7lpjidYi0h7",
                    "type": "User"
                },
                "authenticationContext": {
                    "authenticationProvider": null,
                    "authenticationStep": 0,
                    "credentialProvider": null,
                    "credentialType": null,
                    "externalSessionId": "102ejbJMP0RSE2zwIOhr_PpHA",
                    "interface": null,
                    "issuer": null
                },
                "client": {
                    "device": "Computer",
                    "geographicalContext": {
                        "city": "Tel Aviv",
                        "country": "Israel",
                        "geolocation": {
                            "lat": 32.0678,
                            "lon": 34.7647
                        },
                        "postalCode": null,
                        "state": "Tel Aviv"
                    },
                    "id": null,
                    "ipAddress": "127.0.0.1",
                    "userAgent": {
                        "browser": "CHROME",
                        "os": "Mac OS X",
                        "rawUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
                    },
                    "zone": "null"
                },
                "debugContext": {
                    "debugData": {
                        "authnRequestId": "XYI-PiWDTYEsVNLm7sc0vwAACi0",
                        "initiationType": "SP_INITIATED",
                        "requestId": "XYI-PzNoI1UMTtFvio-9LAAACAc",
                        "requestUri": "/app/demistodev725178_benzi_1/exkm30ffmuhcL0rFv0h7/sso/saml",
                        "signOnMode": "SAML 2.0",
                        "url": "/app/demistodev725178_benzi_1/exkm30ffmuhcL0rFv0h7/sso/saml?RelayState=&SAMLRequest=PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzRiODZjMGUzLWI0NzktNGU2NS00ZjIwLWJiNTE0YTc3NTUyMiIgVmVyc2lvbj0iMi4wIiBQcm90b2NvbEJpbmRpbmc9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpiaW5kaW5nczpIVFRQLVBPU1QiIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly9lYzItNTItNDgtMTItMTIzLmV1LXdlc3QtMS5jb21wdXRlLmFtYXpvbmF3cy5jb20vc2FtbCIgSXNzdWVJbnN0YW50PSIyMDE5LTA5LTE4VDE0OjIyOjM3WiI%2BPHNhbWw6SXNzdWVyPmh0dHBzOi8vZGV2LTcyNTE3OC5va3RhcHJldmlldy5jb20vYXBwL2V4a20zMGZmbXVoY0wwckZ2MGg3L3Nzby9zYW1sL21ldGFkYXRhPC9zYW1sOklzc3Vlcj48c2FtbHA6TmFtZUlEUG9saWN5IEFsbG93Q3JlYXRlPSJ0cnVlIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnBlcnNpc3RlbnQiPjwvc2FtbHA6TmFtZUlEUG9saWN5Pjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg%3D%3D&fromLoginToken=OODncZp5PTgZ-8QwGMHgbJ6psmQES6dUWiMohfLGGm7GWKko8LFXHz3faG7ZkoocPX2ixv-dyUSOF7qJ9DZDVoVkeETM7n6MskWQ01woQFqUcUVdM1xDBmplZlK1DMhX6ozpZQV2XNK073FfDt4bASBEFFgGkFi5ygH-LmBFSfcoiLWHM5MGJ-JEUB97peJxOL41inNX2r333FMJoDzem0dLAOf4cfApoVz7VDdY06r8i6Lt0vuxmxKyZRWJkCHroKyc3ysag9gbUMR5tSoe3hRPJvCBozjYtzkpVlLBP_6V01eGL2YVP8JR2rkpI8MvBYNDLIoJry1e_eBOF3kzJA"
                    }
                },
                "displayMessage": "User single sign on to app",
                "eventType": "user.authentication.sso",
                "legacyEventType": "app.auth.sso",
                "outcome": {
                    "reason": null,
                    "result": "SUCCESS"
                },
                "published": "2019-09-18T14:29:19.329Z",
                "request": {
                    "ipChain": [
                        {
                            "geographicalContext": {
                                "city": "Tel Aviv",
                                "country": "Israel",
                                "geolocation": {
                                    "lat": 32.0678,
                                    "lon": 34.7647
                                },
                                "postalCode": null,
                                "state": "Tel Aviv"
                            },
                            "ip": "127.0.0.1",
                            "source": null,
                            "version": "V4"
                        }
                    ]
                },
                "securityContext": {
                    "asNumber": null,
                    "asOrg": null,
                    "domain": null,
                    "isProxy": null,
                    "isp": null
                },
                "severity": "INFO",
                "target": [
                    {
                        "alternateId": "benzi_master",
                        "detailEntry": {
                            "signOnModeType": "SAML_2_0"
                        },
                        "displayName": "benzi_master",
                        "id": "0oam30ffmvM5cLzxo0h7",
                        "type": "AppInstance"
                    },
                    {
                        "alternateId": "Test@demisto.com",
                        "detailEntry": null,
                        "displayName": "Test Demisto",
                        "id": "0uam30nfffJqV3I4M0h7",
                        "type": "AppUser"
                    }
                ],
                "transaction": {
                    "detail": {},
                    "id": "XYI-PzNoI1UMTtFvio-9LAAACAc",
                    "type": "WEB"
                },
                "uuid": "b349fd35-da20-11e9-81c0-95908eb13131",
                "version": "0"
            }
        }
    }
}
Human Readable Output

Application Authentication Events

Actor ActorAlternaneId ChainIP Client EventInfo EventOutcome EventSeverity RequestIP Targets Time
Test Demisto (User) Test@demisto.com 127.0.0.1 CHROME on Mac OS X Computer User single sign on to app SUCCESS INFO 127.0.0.1 BenziPermanent (AppInstance) Test Demisto (AppUser) 10/14/2019, 12:16:53

okta-delete-user


Deletes the specified user.

Base Command

okta-delete-user

Input

Argument Name Description Required
userId Okta User ID. Optional
username Username of the user. Optional

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-delete-user username=testForDocs@test.com

Human Readable Output

User: testForDocs@test.com was Deleted successfully

okta-clear-user-sessions


Removes all active identity provider sessions. This forces the user to authenticate upon the next operation. By default, OpenID Connect and OAuth refresh and access tokens issued to the user are revoked. Token revocation can be disabled if needed.
For more information and examples:
https://developer.okta.com/docs/reference/api/users/#user-sessions

Base Command

okta-clear-user-sessions

Input

Argument Name Description Required
userId Okta User ID. Required
revokeOauthTokens When true, revokes OpenID Connect and OAuth refresh and access tokens issued to the user. Possible values are: true, false. Default is true. Optional

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).
Command Example

!okta-clear-user-sessions userId=00ui5brmwtJpMdoZZ0h7

Human Readable Output

User session was cleared for: 00ui5brmwtJpMdoZZ0h7

okta-list-zones


Get an Okta Zone object.

Base Command

okta-list-zones

Input

Argument Name Description Required
limit The maximum number of results to return. Optional

Context Output

Path Type Description
Okta.Zone.created Date Zone creation timestamp, in the format 2020-04-06T22:23:12.000Z.
Okta.Zone.gateways.type String Gateways IP entry type, e.g., CIDR.
Okta.Zone.gateways.value String Gateways IP entry value, e.g., 34.103.1.108/32.
Okta.Zone.id String Zone ID, e.g., nzoqsmcx1qWYJ6wYF0h7.
Okta.Zone.lastUpdated Date Zone last update timestamp, e.g., 2020-04-06T22:23:12.000Z.
Okta.Zone.name String Zone name.
Okta.Zone.proxies.type String Proxies IP entry type e.g. CIDR.
Okta.Zone.proxies.value Unknown Proxies IP entry value, e.g., 34.103.1.108/32.
Okta.Zone.status String Zone status, e.g., ACTIVE.
Okta.Zone.system Number True if this is a system zone, false if user-created.
Okta.Zone.type String Zone type, e.g., IP.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).

Command Example


#### Context Example

{
“Okta”: {
“Zone”: [
{
“_links”: {
“deactivate”: {
“hints”: {
“allow”: [
“POST”
]
},
“href”: “https://dev-950355.oktapreview.com/api/v1/zones/nzo9rbw8evGOFV1VE0h7/lifecycle/deactivate”
},
“self”: {
“hints”: {
“allow”: [
“GET”,
“PUT”,
“DELETE”
]
},
“href”: “https://dev-950355.oktapreview.com/api/v1/zones/nzo9rbw8evGOFV1VE0h7”
}
},
“created”: “2017-03-03T22:05:24.000Z”,
“gateways”: [
{
“type”: “CIDR”,
“value”: “2.2.2.2/32”
}
],
“id”: “nzo9rbw8evGOFV1VE0h7”,
“lastUpdated”: “2020-04-23T08:58:55.000Z”,
“name”: “LegacyIpZone”,
“proxies”: null,
“status”: “ACTIVE”,
“system”: true,
“type”: “IP”
},
{
“_links”: {
“deactivate”: {
“hints”: {
“allow”: [
“POST”
]
},
“href”: “https://dev-950355.oktapreview.com/api/v1/zones/nzoqsmcx1qWYJ6wY33h7/lifecycle/deactivate”
},
“self”: {
“hints”: {
“allow”: [
“GET”,
“PUT”,
“DELETE”
]
},
“href”: “https://dev-950355.oktapreview.com/api/v1/zones/nzoqsmcx1qWYJ6wY33h7”
}
},
“created”: “2020-04-06T22:23:12.000Z”,
“gateways”: [
{
“type”: “CIDR”,
“value”: “1.1.1.2/32”
},
{
“type”: “CIDR”,
“value”: “1.1.1.3/32”
},
{
“type”: “CIDR”,
“value”: “2.2.2.2/32”
},
{
“type”: “CIDR”,
“value”: “2.2.2.3/32”
}
],
“id”: “nzoqsmcx1qWYJ6wY33h7”,
“lastUpdated”: “2020-06-05T08:57:57.000Z”,
“name”: “MyZone”,
“proxies”: null,
“status”: “ACTIVE”,
“system”: false,
“type”: “IP”
}
]
}
}


#### Human Readable Output
>
>### Okta Zones
>
>|name|id|gateways|status|system|lastUpdated|created|
>|---|---|---|---|---|---|---|
>| LegacyIpZone | nzo9rbw8evGOFV1VE0h7 | {'type': 'CIDR', 'value': '2.2.2.2/32'} | ACTIVE | true | 2020-04-23T08:58:55.000Z | 2017-03-03T22:05:24.000Z |
>| MyZone | nzoqsmcx1qWYJ6wY33h7 | {'type': 'CIDR', 'value': '3.3.3.4/32'},<br/>{'type': 'CIDR', 'value': '5.5.5.3/32'},<br/>{'type': 'CIDR', 'value': '3.3.3.1/32'},<br/>{'type': 'CIDR', 'value': '2.2.2.3/32'} | ACTIVE | false | 2020-06-05T08:57:57.000Z | 2020-04-06T22:23:12.000Z |

### okta-update-zone

***
Update an Okta Zone.

#### Base Command

`okta-update-zone`

#### Input

| **Argument Name** | **Description**                                                                                                                                         | **Required** |
| --- |---------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
| zoneID | Zone ID to update, e.g., nzoqsmcx1qWYJ6wYF0h7.                                                                                                          | Required |
| zoneName | Updates the zone name.                                                                                                                                  | Optional |
| gatewayIPs | Updates Gateway IP addresses: CIDR range (1.1.0.0/16) or single IP address (2.2.2.2).                                                                   | Optional |
| proxyIPs | Update Proxy IP addresses: CIDR range (1.1.0.0/16) or single IP address (2.2.2.2).                                                                      | Optional |
| updateType | Indicate the action of adding an IP to an existing zone or overriding the existing IPs. Possible values are: "APPEND", "OVERRIDE". Default is "OVERRIDE". | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| Okta.Zone.created | Date | Zone creation timestamp, e.g., 2020-04-06T22:23:12.000Z. |
| Okta.Zone.gateways.type | String | Gateways IP entry type, e.g., CIDR. |
| Okta.Zone.gateways.value | String | Gateways IP entry value, e.g., 34.103.1.108/32. |
| Okta.Zone.id | String | Okta Zone ID, e.g., nzoqsmcx1qWYJ6wYF0h7. |
| Okta.Zone.lastUpdated | Date | Zone last update timestamp, in the format 2020-04-06T22:23:12.000Z. |
| Okta.Zone.name | String | Zone name. |
| Okta.Zone.proxies.type | String | Proxies IP entry type, e.g., CIDR. |
| Okta.Zone.proxies.value | Unknown | Proxies IP entry value, e.g., 34.103.1.108/32. |
| Okta.Zone.status | String | Zone status, e.g., ACTIVE. |
| Okta.Zone.system | Number | True if this is a system zone, false if user-created. |
| Okta.Zone.type | String | Zone type, e.g., IP. |
| Okta.Metadata.x-rate-limit-limit | Number | The rate limit ceiling that’s applicable for the current request. |
| Okta.Metadata.x-rate-limit-remaining | Number | The amount of requests left for the current rate-limit window. |
| Okta.Metadata.x-rate-limit-reset | Number | The time at which the rate limit resets, specified in UTC epoch time (in seconds). |

#### Command Example

```!okta-update-zone zoneID=nzoqsmcx1qWYJ6wY33h7 zoneName=MyZone```

#### Context Example

{
“Okta”: {
“Zone”: {
“_links”: {
“deactivate”: {
“hints”: {
“allow”: [
“POST”
]
},
“href”: “https://dev-950355.oktapreview.com/api/v1/zones/nzoqsmcx1qWYJ6wY33h7/lifecycle/deactivate”
},
“self”: {
“hints”: {
“allow”: [
“GET”,
“PUT”,
“DELETE”
]
},
“href”: “https://dev-950355.oktapreview.com/api/v1/zones/nzoqsmcx1qWYJ6wY33h7”
}
},
“created”: “2020-04-06T22:23:12.000Z”,
“gateways”: [
{
“type”: “CIDR”,
“value”: “1.1.3.5/32”
},
{
“type”: “CIDR”,
“value”: “5.3.143.103/32”
},
{
“type”: “CIDR”,
“value”: “5.3.246.228/32”
},
{
“type”: “CIDR”,
“value”: “5.3.246.229/32”
}
],
“id”: “nzoqsmcx1qWYJ6wY33h7”,
“lastUpdated”: “2020-06-05T08:57:57.000Z”,
“name”: “MyZone”,
“proxies”: null,
“status”: “ACTIVE”,
“system”: false,
“type”: “IP”
}
}
}


#### Human Readable Output

>### Okta Zones
>
>|name|id|gateways|status|system|lastUpdated|created|
>|---|---|---|---|---|---|---|
>| MyZone | nzoqsmcx1qWYJ6wY33h7 | {'type': 'CIDR', 'value': '1.3.1.5/32'},<br/>{'type': 'CIDR', 'value': '1.3.1.5/32'},<br/>{'type': 'CIDR', 'value': '1.3.1.5/32'},<br/>{'type': 'CIDR', 'value': '1.3.1.5/32'} | ACTIVE | false | 2020-06-05T08:57:57.000Z | 2020-04-06T22:23:12.000Z |

### okta-get-zone

***
Get a Zone by its ID.

#### Base Command

`okta-get-zone`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| zoneID | Zone ID to get, e.g., nzoqsmcx1qWYJ6wYF0h.7. | Required |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| Okta.Zone.created | Date | Zone creation timestamp, in the format 2020-04-06T22:23:12.000Z. |
| Okta.Zone.gateways.type | String | Gateways IP entry type, e.g., CIDR. |
| Okta.Zone.gateways.value | String | Gateways IP entry value, e.g., 34.103.1.108/32. |
| Okta.Zone.id | String | Okta Zone ID, e.g., nzoqsmcx1qWYJ6wYF0h7. |
| Okta.Zone.lastUpdated | Date | Zone last update timestamp, in the format 2020-04-06T22:23:12.000Z. |
| Okta.Zone.name | String | Zone name. |
| Okta.Zone.proxies.type | String | Proxies IP entry type, e.g., CIDR. |
| Okta.Zone.proxies.value | Unknown | Proxies IP entry value, e.g., 34.103.1.108/32. |
| Okta.Zone.status | String | Zone status, e.g,. ACTIVE. |
| Okta.Zone.system | Number | True if this is a system zone, false if user-created. |
| Okta.Zone.type | String | Zone type, e.g., IP. |
| Okta.Metadata.x-rate-limit-limit | Number | The rate limit ceiling that’s applicable for the current request. |
| Okta.Metadata.x-rate-limit-remaining | Number | The amount of requests left for the current rate-limit window. |
| Okta.Metadata.x-rate-limit-reset | Number | The time at which the rate limit resets, specified in UTC epoch time (in seconds). |

#### Command Example

```!okta-get-zone zoneID=nzoqsmcx1qWYJ6wY33h7```

#### Context Example

{
“Okta”: {
“Zone”: {
“_links”: {
“deactivate”: {
“hints”: {
“allow”: [
“POST”
]
},
“href”: “https://dev-950355.oktapreview.com/api/v1/zones/nzoqsmcx1qWYJ6wY33h7/lifecycle/deactivate”
},
“self”: {
“hints”: {
“allow”: [
“GET”,
“PUT”,
“DELETE”
]
},
“href”: “https://dev-950355.oktapreview.com/api/v1/zones/nzoqsmcx1qWYJ6wY33h7”
}
},
“created”: “2020-04-06T22:23:12.000Z”,
“gateways”: [
{
“type”: “CIDR”,
“value”: “1.3.1.3/32”
},
{
“type”: “CIDR”,
“value”: “3.5.146.103/32”
},
{
“type”: “CIDR”,
“value”: “3.5.1.228/32”
},
{
“type”: “CIDR”,
“value”: “3.5.1.229/32”
}
],
“id”: “nzoqsmcx1qWYJ6wY33h7”,
“lastUpdated”: “2020-06-05T08:57:57.000Z”,
“name”: “MyZone”,
“proxies”: null,
“status”: “ACTIVE”,
“system”: false,
“type”: “IP”
}
}
}


#### Human Readable Output

>### Okta Zones
>
>|name|id|gateways|status|system|lastUpdated|created|
>|---|---|---|---|---|---|---|
>| MyZone | nzoqsmcx1qWYJ6wY33h7 | {'type': 'CIDR', 'value': '1.3.1.3/32'},<br/>{'type': 'CIDR', 'value': '3.5.146.103/32'},<br/>{'type': 'CIDR', 'value': '3.5.1.228/32'},<br/>{'type': 'CIDR', 'value': '3.5.1.229/32'} | ACTIVE | false | 2020-06-05T08:57:57.000Z | 2020-04-06T22:23:12.000Z |

### okta-list-users

***
Lists users in your organization.

#### Base Command

`okta-list-users`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| verbose | Whether to return extended user information. Can be "true" or "false". The default is "false". Possible values are: true, false. Default is false. | Optional |
| limit | The maximum number of results to return. | Optional |
| query | Searches the name property of groups for matching values. | Optional |
| filter | Useful for performing structured queries where constraints on group attribute values can be explicitly targeted. <br/>The following expressions are supported(among others) for groups with the filter query parameter: <br/>type eq "OKTA_GROUP" - Groups that have a type of OKTA_GROUP; lastUpdated lt "yyyy-MM-dd''T''HH:mm:ss.SSSZ" - Groups with profile last updated before a specific timestamp; lastMembershipUpdated eq "yyyy-MM-dd''T''HH:mm:ss.SSSZ" - Groups with memberships last updated at a specific timestamp; id eq "00g1emaKYZTWRYYRRTSK" - Group with a specified ID.
For more information about filtering, visit https://developer.okta.com/docs/api/getting_started/design_principles#filtering. | Optional |
| after | The cursor in which to retrive the results from and on. If the query didn't reach the end of results, the tag will be found in the readable output under the tag key. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| Account.ID | String | Okta account ID. |
| Account.Email | String | Okta account email. |
| Account.Username | String | Okta account username. |
| Account.DisplayName | String | Okta account display name. |
| Account.Status | String | Okta account status. |
| Account.Created | Date | Timestamp for when the user was created. |
| Account.Activated | Date | Timestamp for when the user was activated. |
| Account.StatusChanged | Date | Timestamp for when the user's status was last changed. |
| Account.PasswordChanged | Date | Timestamp for when the user's password was last changed. |
| Okta.User.tag| String | The location of the next item, used with after param. |
| Okta.Metadata.x-rate-limit-limit | Number | The rate limit ceiling that’s applicable for the current request. |
| Okta.Metadata.x-rate-limit-remaining | Number | The amount of requests left for the current rate-limit window. |
| Okta.Metadata.x-rate-limit-reset | Number | The time at which the rate limit resets, specified in UTC epoch time (in seconds). |

#### Command Example

```!okta-list-users```

#### Context Example

```json
{
    "Okta":
    {
        "User":
        {
            "tag": "test12tag"
        }
    },
    "Account": [
        {
            "Created": "2018-07-24T20:20:04.000Z",
            "DisplayName": "Dbot XSOAR",
            "Email": "dbot@xsoar.com",
            "ID": "XXXXXXXXX",
            "Status": "STAGED",
            "Type": "Okta",
            "Username": "dbot@xsoar.com"
        }
    ]
}

Human Readable Output

Okta users found

Users

First Name ID Last Login Last Name Login Mobile Phone Status
Dbot XXXXX   XSOAR dbot@xsoar.com   STAGED

tag: test12tag

okta-create-zone


Creates a Zone with the specified name.

Base Command

okta-create-zone

Input

Argument Name Description Required
name Zone name. Required
gateway_ips Update Gateway IP addresses: CIDR range (1.1.0.0/16) or single IP address (2.2.2.2). Optional
proxies Update Proxy IP addresses: CIDR range (1.1.0.0/16) or single IP address (2.2.2.2). Optional

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).

okta-create-group


Create a new group in Okta tenant.

Base Command

okta-create-group

Input

Argument Name Description Required
name Name of the group to add. Required
description Description of the group to add. Optional

Context Output

Path Type Description
OktaGroup.ID Unknown Group ID in Okta.
OktaGroup.Name Unknown Group name in Okta.
OktaGroup.Description Unknown Group description in Okta.
OktaGroup.Type Unknown Group type in Okta.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).

Command example

!okta-create-group name="TestGroup" description="TestGroup description."

Context Example

{
    "OktaGroup": {
        "Description": "TestGroup description.",
        "ID": "00g3qb398kItYXzKd1d7",
        "Name": "TestGroup",
        "Type": "OKTA_GROUP"
    }
}

Human Readable Output

Group Created: [GroupID:00g3qb398kItYXzKd1d7, GroupName: TestGroup]

okta-assign-group-to-app


Assign a group to an application.

Base Command

okta-assign-group-to-app

Input

Argument Name Description Required
groupName Name of the group to assign to the app. Optional
groupId ID of the group to assign to the app. Optional
appName Friendly name of the app that the group will be assigned to. Optional

Context Output

Path Type Description
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).

Command example

!okta-assign-group-to-app appName="Default-App" groupName="TestGroup"

Human Readable Output

Group: TestGroup added to PA App successfully

okta-expire-password


Expires a password for an existing Okta user.

Base Command

okta-expire-password

Input

Argument Name Description Required
username Okta username for which to expire the password. Required
temporary_password When true, you’ll need to change the password in the next login. Possible values are: true, false. Default is false. Optional
revoke_session When true, revokes the user’s existing sessions. Optional
hide_password When true, prevents the password from being saved in the war room. Optional

Context Output

Path Type Description
Account.Activated Date Timestamp for when the user was activated.
Account.Created Date Timestamp for when the user was created.
Account.DisplayName String Okta account display name.
Account.Email String Okta account email.
Account.ID String Created Okta account ID.
Account.PasswordChanged Date Timestamp for when the user’s password was last changed.
Account.Status String Okta account current status.
Account.StatusChanged Date Timestamp for when the user’s status was last changed.
Account.Type String Okta account type.
Account.Username String Okta account usernames returned by the search.
Okta.Metadata.x-rate-limit-limit Number The rate limit ceiling that’s applicable for the current request.
Okta.Metadata.x-rate-limit-remaining Number The amount of requests left for the current rate-limit window.
Okta.Metadata.x-rate-limit-reset Number The time at which the rate limit resets, specified in UTC epoch time (in seconds).

Command example

!okta-expire-password username="4x1xh5rl@test.com" temporary_password="false"

Context Example

{
    "Account": {
        "Activated": "2022-06-20T04:48:04.000Z",
        "Created": "2022-06-20T04:47:59.000Z",
        "DisplayName": "Test 1  Test1",
        "Email": "4x1xh5rl@test.com",
        "ID": "00u19cr5qv91HjELI0h8",
        "PasswordChanged": "2022-06-20T04:48:07.000Z",
        "Status": "PASSWORD_EXPIRED",
        "StatusChanged": "2023-09-10T12:56:04.000Z",
        "Type": "Okta",
        "Username": "4x1xh5rl@test.com"
    }
}

Human Readable Output

Okta Expired Password

_links activated created credentials id lastUpdated passwordChanged profile status statusChanged type
suspend: {“href”: “https://test.oktapreview.com/api/v1/users/00u19cr5qv91HjELI0h8/lifecycle/suspend”, “method”: “POST”}
schema: {“href”: “https://test.oktapreview.com/api/v1/meta/schemas/user/osc66lckcvDyVcGzS0h7”}
resetPassword: {“href”: “https://test.oktapreview.com/api/v1/users/00u19cr5qv91HjELI0h8/lifecycle/reset_password”, “method”: “POST”}
forgotPassword: {“href”: “https://test.oktapreview.com/api/v1/users/00u19cr5qv91HjELI0h8/credentials/forgot_password”, “method”: “POST”}
expirePassword: {“href”: “https://test.oktapreview.com/api/v1/users/00u19cr5qv91HjELI0h8/lifecycle/expire_password”, “method”: “POST”}
changeRecoveryQuestion: {“href”: “https://test.oktapreview.com/api/v1/users/00u19cr5qv91HjELI0h8/credentials/change_recovery_question”, “method”: “POST”}
self: {“href”: “https://test.oktapreview.com/api/v1/users/00u19cr5qv91HjELI0h8”}
type: {“href”: “https://test.oktapreview.com/api/v1/meta/types/user/oty66lckcvDyVcGzS0h7”}
changePassword: {“href”: “https://test.oktapreview.com/api/v1/users/00u19cr5qv91HjELI0h8/credentials/change_password”, “method”: “POST”}
deactivate: {“href”: “https://test.oktapreview.com/api/v1/users/00u19cr5qv91HjELI0h8/lifecycle/deactivate”, “method”: “POST”}
2022-06-20T04:48:04.000Z 2022-06-20T04:47:59.000Z password: {}
recovery_question: {“question”: “whats the first school?”}
provider: {“type”: “OKTA”, “name”: “OKTA”}
00u19cr5qv91HjELI0h8 2023-09-10T12:56:04.000Z 2022-06-20T04:48:07.000Z firstName: Test 1
lastName: Test1
preferredLanguage: en
mobilePhone: null
city: Tel-Aviv
displayName: Test 1 that
nickName: Testush
secondEmail: null
login: 4x1xh5rl@test.com
email: 4x1xh5rl@test.com
employeeNumber: 12345
PASSWORD_EXPIRED 2023-09-10T12:56:04.000Z id: oty66lckcvDyVcGzS0h7

okta-auth-reset


Reset OAuth authentication data (authentication process will start from the beginning, and a new token will be generated).

Base Command

okta-auth-reset

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Configuration parameters

  • url — Okta URL (https://<domain>.okta.com) (required)
  • credentials
  • apitoken — API Token
  • use_oauth — Use OAuth 2.0 Authentication
  • client_id — Client ID
  • private_key — Private Key
  • jwt_algorithm — JWT Signing Algorithm
  • key_id — Key ID
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (35)

  • okta-activate-user

    Activates a single user.

  • okta-add-to-group

    Adds a user to a group with OKTA_GROUP type.

  • okta-assign-group-to-app

    Assign a group to an application.

  • okta-auth-reset

    Reset OAuth authentication data (authentication process will start from the beginning, and a new token will be generated).

  • okta-clear-user-sessions

    Removes all active identity provider sessions. This forces the user to authenticate upon the next operation. By default, OpenID Connect and OAuth refresh and access tokens issued to the user are revoked. Token revocation can be disabled if needed. For more information and examples: https://developer.okta.com/docs/reference/api/users/#user-sessions

  • okta-create-group

    Create a new group in Okta tenant.

  • okta-create-user

    Creates a new user with an option of setting a password, recovery question, and answer. The new user will immediately be able to log in after activation with the assigned password. This flow is common when developing a custom user registration experience.

  • okta-create-zone

    Creates a Zone with the specified name.

  • okta-deactivate-user

    Deactivates a single user.

  • okta-delete-user

    Deletes the specified user.

  • okta-expire-password

    Expires a password for an existing Okta user.

  • okta-get-application-assignments

    Returns events for when a user was assigned to an application.

  • okta-get-application-authentication

    Returns logs using specified filters.

  • okta-get-failed-logins

    Returns failed login events.

  • okta-get-group-assignments

    Gets events for when a user was added to a group.

  • okta-get-group-members

    Enumerates all users that are members of a group.

  • okta-get-groups

    Returns all user groups associated with a specified user.

  • okta-get-logs

    Gets logs by providing optional filters.

  • okta-get-user

    Fetches information for a single user. You must enter one or more parameters for the command to run.

  • okta-get-user-factors

    Returns all the enrolled factors for the specified user.

  • okta-get-zone

    Get a Zone by its ID.

  • okta-list-groups

    Lists groups in your organization. A subset of groups can be returned that match a supported filter expression or query.

  • okta-list-users

    Lists users in your organization.

  • okta-list-zones

    Get an Okta Zone object.

  • okta-remove-from-group

    Removes a user from a group with OKTA_GROUP type.

  • okta-reset-factor

    Un-enrolls an existing factor for the specified user. This enables the user to enroll a new factor.

  • okta-search

    Searches for Okta users.

  • okta-set-password

    Sets passwords without validating existing user credentials.

  • okta-suspend-user

    Suspends a single user. This operation can only be performed on users with an ACTIVE status. After the porcess is completed, the user's status is SUSPENDED.

  • okta-unlock-user

    Unlocks a single user.

  • okta-unsuspend-user

    Returns a single user to ACTIVE status. This operation can only be performed on users that have a SUSPENDED status.

  • okta-update-user

    Updates a user with a given login. All fields are optional. Fields which are not set, will not be overwritten.

  • okta-update-zone

    Update an Okta Zone.

  • okta-verify-mfa-status

    Checks the status of the push factor mfa.

  • okta-verify-push-factor

    Enrolls and verifies a push factor for the specified user.

from urllib.parse import parse_qs, urlencode, urlparse, urlunparse

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401
from OktaApiModule import *  # noqa: E402

# CONSTANTS
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
SEARCH_LIMIT = 200
OAUTH_TOKEN_SCOPES = [  # Scopes to request when generating an OAuth token
    "okta.apps.manage",
    "okta.apps.read",
    "okta.groups.manage",
    "okta.groups.read",
    "okta.logs.read",
    "okta.networkZones.manage",
    "okta.networkZones.read",
    "okta.sessions.manage",
    "okta.sessions.read",
    "okta.users.manage",
    "okta.users.read",
]
PROFILE_ARGS = [
    "firstName",
    "lastName",
    "email",
    "login",
    "secondEmail",
    "middleName",
    "honorificPrefix",
    "honorificSuffix",
    "title",
    "displayName",
    "nickName",
    "profileUrl",
    "primaryPhone",
    "mobilePhone",
    "streetAddress",
    "city",
    "state",
    "zipCode",
    "countryCode",
    "postalAddress",
    "preferredLanguage",
    "locale",
    "timezone",
    "userType",
    "employeeNumber",
    "costCenter",
    "organization",
    "division",
    "department",
    "managerId",
    "manager",
]
GROUP_PROFILE_ARGS = ["name", "description"]

MAX_LOGS_LIMIT = 1000
DEFAULT_POLLING_TIME = 5
DEFAULT_MAX_POLLING_CALLS = 10


class Client(OktaClient):
    # Getting Group Id with a given group name
    def get_group_id(self, group_name):
        uri = "/api/v1/groups"
        query_params = {"q": encode_string_results(group_name)}
        res = self.http_request(method="GET", url_suffix=uri, params=query_params)
        return next((r.get("id") for r in (res or []) if r.get("profile", {}).get("name") == group_name), None)

    def get_app_id(self, app_name):
        uri = "/api/v1/apps"
        query_params = {"q": encode_string_results(app_name)}
        res = self.http_request(method="GET", url_suffix=uri, params=query_params)
        if res and len(res) == 1:
            return res[0].get("id")
        return None

    # Getting User Id with a given username
    def get_user_id(self, username):
        uri = "/api/v1/users"
        query_params = {"filter": encode_string_results(f'profile.login eq "{username}"')}
        res = self.http_request(method="GET", url_suffix=uri, params=query_params)
        if res and len(res) == 1:
            return res[0].get("id")
        raise Exception(f"Failed to find userID for: {username} username.")

    def unlock_user(self, user_id):
        """
        sending a POST request to unlock a specific user
        """
        uri = f"/api/v1/users/{user_id}/lifecycle/unlock"
        return self.http_request(method="POST", url_suffix=uri)

    def deactivate_user(self, user_id):
        uri = f"/api/v1/users/{user_id}/lifecycle/deactivate"
        return self.http_request(method="POST", url_suffix=uri)

    def activate_user(self, user_id):
        uri = f"/api/v1/users/{user_id}/lifecycle/activate"
        return self.http_request(method="POST", url_suffix=uri)

    def suspend_user(self, user_id):
        uri = f"/api/v1/users/{user_id}/lifecycle/suspend"
        return self.http_request(method="POST", url_suffix=uri)

    def unsuspend_user(self, user_id):
        uri = f"/api/v1/users/{user_id}/lifecycle/unsuspend"
        return self.http_request(method="POST", url_suffix=uri)

    def get_user_factors(self, user_id):
        uri = f"/api/v1/users/{user_id}/factors"
        return self.http_request(method="GET", url_suffix=uri)

    def reset_factor(self, user_id, factor_id):
        uri = f"/api/v1/users/{user_id}/factors/{factor_id}"
        return self.http_request(method="DELETE", url_suffix=uri, resp_type="text")

    def set_password(self, user_id, password):
        uri = f"/api/v1/users/{user_id}"
        body = {"credentials": {"password": {"value": password}}}

        return self.http_request(method="POST", url_suffix=uri, json_data=body)

    def revoke_session(self, user_id):
        uri = f"/api/v1/users/{user_id}/lifecycle/expire_password_with_temp_password"
        params = {"revokeSessions": "true"}
        return self.http_request(method="POST", url_suffix=uri, params=params)

    def expire_password(self, user_id, args):
        uri = f"/api/v1/users/{user_id}/lifecycle/expire_password"
        params = {"tempPassword": args.get("temporary_password", "false")}
        return self.http_request(method="POST", url_suffix=uri, params=params)

    def add_user_to_group(self, user_id, group_id):
        uri = f"/api/v1/groups/{group_id}/users/{user_id}"
        return self.http_request(method="PUT", url_suffix=uri, resp_type="text")

    def remove_user_from_group(self, user_id, group_id):
        uri = f"/api/v1/groups/{group_id}/users/{user_id}"
        return self.http_request(method="DELETE", url_suffix=uri, resp_type="text")

    def get_groups_for_user(self, user_id):
        uri = f"/api/v1/users/{user_id}/groups"
        return self.http_request(method="GET", url_suffix=uri)

    @staticmethod
    def get_readable_groups(raw_groups):
        groups = []
        raw_groups = raw_groups if isinstance(raw_groups, list) else [raw_groups]
        for group in raw_groups:
            group = {
                "ID": group.get("id"),
                "Created": group.get("created"),
                "ObjectClass": group.get("objectClass"),
                "LastUpdated": group.get("lastUpdated"),
                "LastMembershipUpdated": group.get("lastMembershipUpdated"),
                "Type": group.get("type"),
                "Name": group.get("profile", {}).get("name"),
                "Description": group.get("profile", {}).get("description"),
            }
            groups.append(group)
        return groups

    @staticmethod
    def get_readable_logs(raw_logs):
        logs = []
        browser = ""
        device = ""
        os = ""
        raw_logs = raw_logs if isinstance(raw_logs, list) else [raw_logs]
        for log in raw_logs:
            if log.get("client", {}).get("userAgent"):
                browser = log.get("client", {}).get("userAgent").get("browser")
                if (not browser) or browser.lower() == "unknown":
                    browser = "Unknown browser"
                os = log.get("client", {}).get("userAgent").get("os")
                if (not os) or os.lower() == "unknown":
                    os = "Unknown OS"
                device = log.get("client", {}).get("device")
                if (not device) or device.lower() == "unknown":
                    device = "Unknown device"
            targets = ""
            if log.get("target"):
                for target in log.get("target"):
                    targets += f"{target.get('displayName')} ({target.get('type')})\n"
            time_published = datetime.strptime(log.get("published"), "%Y-%m-%dT%H:%M:%S.%f%z").strftime("%m/%d/%Y, %H:%M:%S")
            log = {
                "Actor": f"{log.get('actor', {}).get('displayName')} ({log.get('actor', {}).get('type')})",
                "ActorAlternaneId": log.get("actor", {}).get("alternateId"),
                "EventInfo": log.get("displayMessage"),
                "EventOutcome": log.get("outcome", {}).get("result")
                + (f": {log.get('outcome', {}).get('reason')}" if log.get("outcome", {}).get("reason") else ""),
                "EventSeverity": log.get("severity"),
                "Client": f"{browser} on {os} {device}" if browser else "Unknown client",
                "RequestIP": log.get("client", {}).get("ipAddress"),
                "ChainIP": [ip_chain.get("ip") for ip_chain in log.get("request", {}).get("ipChain", [])],
                "Targets": targets or "-",
                "Time": time_published,
            }
            logs.append(log)
        return logs

    @staticmethod
    def get_readable_factors(raw_factors):
        factors = []
        for factor in raw_factors:
            factor = {
                "ID": factor.get("id"),
                "FactorType": factor.get("factorType"),
                "Provider": factor.get("provider"),
                "Status": factor.get("status"),
                "Profile": factor.get("profile"),
            }
            factors.append(factor)
        return factors

    def verify_push_factor(self, user_id, factor_id):
        """
        Creates a new transaction and sends an asynchronous push notification to the device for the user to approve or reject.
        You must poll the transaction to determine when it completes or expires.
        """
        uri = f"/api/v1/users/{user_id}/factors/{factor_id}/verify"
        return self.http_request(method="POST", url_suffix=uri)

    def poll_verify_push(self, url, polling_time: int = DEFAULT_POLLING_TIME, max_polling_calls: int = DEFAULT_MAX_POLLING_CALLS):
        """
        Keep polling authentication transactions with WAITING result until the challenge completes or expires.
        time limit defined by us = one minute
        """
        counter = 0
        while counter < max_polling_calls:
            response = self.http_request(method="GET", full_url=url, url_suffix="")
            if response.get("factorResult") != "WAITING":
                return response
            counter += 1
            time.sleep(polling_time)  # pylint: disable=E9003
        response["factorResult"] = "TIMEOUT"
        return response

    def get_push_factor_status(self, polling_url: str):
        """Get the status of the push factor"""
        response = self.http_request(method="GET", full_url=polling_url, url_suffix="")
        return response

    def search(self, term, limit, advanced_search):
        uri = "/api/v1/users"
        query_params = assign_params(limit=limit, q=encode_string_results(term), search=encode_string_results(advanced_search))
        return self.http_request(method="GET", url_suffix=uri, params=query_params)

    @staticmethod
    def get_users_context(raw_users):
        users = []
        raw_users = raw_users if isinstance(raw_users, list) else [raw_users]
        for user in raw_users:
            user = {
                "ID": user.get("id"),
                "Username": user.get("profile", {}).get("login"),
                "DisplayName": f"{user.get('profile', {}).get('firstName', '')} {user.get('profile', {}).get('lastName', '')}",
                "Email": user.get("profile", {}).get("email"),
                "Status": user.get("status"),
                "Type": "Okta",
                "Created": user.get("created"),
                "Activated": user.get("activated"),
                "StatusChanged": user.get("statusChanged"),
                "PasswordChanged": user.get("passwordChanged"),
                "Manager": user.get("profile", {}).get("manager"),
                "ManagerEmail": user.get("profile", {}).get("managerEmail"),
            }
            if user.get("group"):
                user["Group"] = user.get("group")
            users.append(user)
        return users

    @staticmethod
    def get_apps_context(raw_apps):
        apps = []
        raw_apps = raw_apps if isinstance(raw_apps, list) else [raw_apps]
        for app in raw_apps:
            app = {
                "ID": app.get("id"),
            }
            apps.append(app)
        return apps

    @staticmethod
    def get_groups_context(raw_groups):
        groups = []
        raw_groups = raw_groups if isinstance(raw_groups, list) else [raw_groups]
        for group in raw_groups:
            group = {
                "ID": group.get("id"),
                "Name": group.get("profile", {}).get("name"),
                "Description": group.get("profile", {}).get("description"),
                "Type": group.get("type"),
            }
            groups.append(group)
        return groups

    @staticmethod
    def get_readable_users(raw_users, verbose="false"):
        raw_users = raw_users if isinstance(raw_users, list) else [raw_users]
        if verbose == "true":
            users_verbose = ""
            for user in raw_users:
                profile = {
                    "First Name": user.get("profile", {}).get("firstName"),
                    "Last Name": user.get("profile", {}).get("lastName"),
                    "Mobile Phone": user.get("profile", {}).get("mobilePhone"),
                    "Login": user.get("profile", {}).get("login"),
                    "Email": user.get("profile", {}).get("email"),
                    "Second Email": user.get("profile", {}).get("secondEmail"),
                    "Manager": user.get("profile", {}).get("manager"),
                    "Manager Email": user.get("profile", {}).get("managerEmail"),
                }
                additionalData = {
                    "ID": user.get("id"),
                    "Status": user.get("status"),
                    "Created": user.get("created"),
                    "Activated": user.get("activated"),
                    "Status Changed": user.get("userChanged"),
                    "Last Login": user.get("lastLogin"),
                    "Last Updated": user.get("lastUpdated"),
                    "Password Changed": user.get("passwordChanged"),
                    "Type": user.get("type"),
                    "Credentials": user.get("credentials"),
                    "_links": user.get("_links"),
                }
                if user.get("group"):
                    additionalData["Group"] = user.get("group")
                users_verbose += (
                    f"### User:{profile.get('Login')}\n"
                    f"{tableToMarkdown('Profile', profile)}\n {tableToMarkdown('Additional Data', additionalData)}"
                )
            return users_verbose

        else:
            users = []
            for user in raw_users:
                user = {
                    "ID": user.get("id"),
                    "Login": user.get("profile").get("login"),
                    "First Name": user.get("profile").get("firstName"),
                    "Last Name": user.get("profile").get("lastName"),
                    "Mobile Phone": user.get("profile").get("mobilePhone"),
                    "Last Login": user.get("lastLogin"),
                    "Status": user.get("status"),
                    "Manager": user.get("profile", {}).get("manager"),
                    "Manager Email": user.get("profile", {}).get("managerEmail"),
                }
                users.append(user)
            return users

    def get_user(self, user_term):
        uri = f"/api/v1/users/{encode_string_results(user_term)}"
        return self.http_request(method="GET", url_suffix=uri)

    def create_user(self, cred, profile, group_ids, activate):
        body = {"profile": profile, "groupIds": group_ids or [], "credentials": cred}
        uri = "/api/v1/users"
        query_params = {"activate": activate, "provider": "true" if cred.get("provider") else None}
        return self.http_request(method="POST", url_suffix=uri, json_data=body, params=query_params)

    def create_group(self, profile):
        body = {
            "profile": profile,
        }
        uri = "/api/v1/groups"
        return self.http_request(method="POST", url_suffix=uri, json_data=body)

    # Build profile dict with pre-defined keys (for user)
    @staticmethod
    def build_profile(args):
        profile = {}
        keys = args.keys()
        for key in PROFILE_ARGS:
            if key in keys:
                profile[key] = args[key]
        return profile

    # Build profile dict with pre-defined keys (for group)
    @staticmethod
    def build_group_profile(args):
        profile = {}
        keys = args.keys()
        for key in GROUP_PROFILE_ARGS:
            if key in keys:
                profile[key] = args[key]
        return profile

    # Build credentials dict with predefined keys (for user)
    @staticmethod
    def build_credentials(args):
        cred = {}
        if args.get("password"):
            cred["password"] = {"value": args.get("password")}
        if args.get("passwordQuestion") and args.get("passwordAnswer"):
            cred["recovery_question"] = {"question": args.get("passwordQuestion"), "answer": args.get("passwordAnswer")}
        if args.get("providerName") and args.get("providerType"):
            cred["provider"] = {"name": args.get("providerName"), "type": args.get("providerType")}
        return cred

    def update_user(self, user_id, profile, cred):
        body = {"profile": profile, "credentials": cred}
        uri = f"/api/v1/users/{user_id}"
        return self.http_request(method="POST", url_suffix=uri, json_data=body)

    def get_paged_results(self, uri, query_param=None, max_limit=None):
        response = self.http_request(method="GET", url_suffix=uri, resp_type="response", params=query_param)
        paged_results = response.json()
        while "next" in response.links and len(response.json()) > 0:
            next_page = response.links.get("next").get("url")
            response = self.http_request(
                method="GET", full_url=next_page, url_suffix="", resp_type="response", params=query_param
            )
            paged_results += response.json()
            if max_limit and len(paged_results) >= max_limit:
                return paged_results[:max_limit]
        return paged_results

    def get_group_members(self, group_id, limit):
        uri = f"/api/v1/groups/{group_id}/users"
        if limit:
            query_params = {"limit": limit}
            return self.http_request(method="GET", url_suffix=uri, params=query_params)
        return self.get_paged_results(uri)

    def list_users(self, args):
        # Base url - if none of the above specified - returns all the users (default 200 items)
        uri = "/api/v1/users"
        query_params = {}
        for key, value in args.items():
            if key == "query":
                key = "q"
            query_params[key] = encode_string_results(value)
        limit = int(args.get("limit"))
        response = self.http_request(method="GET", url_suffix=uri, resp_type="response", params=query_params)
        paged_results = response.json()
        if limit > 200:
            query_params = {}
            limit -= 200
            while limit > 0 and "next" in response.links and len(response.json()) > 0:
                query_params["limit"] = encode_string_results(str(limit))
                next_page = delete_limit_param(response.links.get("next").get("url"))
                response = self.http_request(
                    method="GET", full_url=next_page, url_suffix="", resp_type="response", params=query_params
                )
                paged_results += response.json()
                limit -= 200
        after = None
        if "next" in response.links and len(response.json()) > 0:
            after = get_after_tag(response.links.get("next").get("url"))
        return (paged_results, after)

    def list_groups(self, args):
        # Base url - if none of the the above specified - returns all the groups (default 200 items)
        uri = "/api/v1/groups"
        query_params = {}
        for key, value in args.items():
            if key == "query":
                key = "q"
            query_params[key] = encode_string_results(value)
        if args.get("limit"):
            return self.http_request(method="GET", url_suffix=uri, params=query_params)
        return self.get_paged_results(uri, query_params)

    def get_logs(self, args):
        uri = "/api/v1/logs"
        query_params = {}
        for key, value in args.items():
            if key == "query":
                key = "q"
            query_params[key] = encode_string_results(value)
        limit = args.get("limit")
        limit = int(limit) if limit else None
        if limit and limit <= MAX_LOGS_LIMIT:
            return self.http_request(method="GET", url_suffix=uri, params=query_params)
        if limit and limit > MAX_LOGS_LIMIT:
            query_params["limit"] = MAX_LOGS_LIMIT
        return self.get_paged_results(uri, query_params, max_limit=limit)

    def delete_user(self, user_term):
        uri = f"/api/v1/users/{encode_string_results(user_term)}"
        return self.http_request(method="DELETE", url_suffix=uri, resp_type="text")

    def clear_user_sessions(self, user_id, revoke_oauth_tokens=True):
        uri = f"/api/v1/users/{user_id}/sessions"
        params = {"oauthTokens": revoke_oauth_tokens}
        return self.http_request(method="DELETE", url_suffix=uri, params=params, resp_type="text")

    def get_zone(self, zoneID):
        uri = f"/api/v1/zones/{zoneID}"
        return self.http_request(method="GET", url_suffix=uri)

    def list_zones(self, limit):
        uri = "/api/v1/zones"
        if limit:
            query_params = {"limit": encode_string_results(limit)}
            return self.http_request(method="GET", url_suffix=uri, params=query_params)
        return self.get_paged_results(uri)

    def create_zone(self, zoneObject):
        uri = "/api/v1/zones"
        return self.http_request(method="POST", url_suffix=uri, json_data=zoneObject)

    def update_zone(self, zoneObject):
        zoneID = zoneObject["id"]
        uri = f"/api/v1/zones/{zoneID}"

        return self.http_request(method="PUT", url_suffix=uri, data=json.dumps(zoneObject))

    def assign_group_to_app(self, group_id, app_id):
        uri = f"/api/v1/apps/{app_id}/groups/{group_id}"
        return self.http_request(method="PUT", url_suffix=uri, resp_type="text")


def module_test(client, args):
    if client.auth_type == AuthType.OAUTH:
        # For OAuth 2.0, there's no user the token belongs to, but an app. So the '/users/me' endpoint won't work.
        uri = "/api/v1/users"

    else:
        uri = "/api/v1/users/me"

    client.http_request(method="GET", url_suffix=uri)
    return "ok", None, None


def unlock_user_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    raw_response = client.unlock_user(user_id)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }

    readable_output = f"### {args.get('username')} unlocked"

    return (
        readable_output,
        outputs,
        raw_response,  # raw response - the original response
    )


def activate_user_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    raw_response = client.activate_user(user_id)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }

    readable_output = f"### {args.get('username')} is active now"
    return (readable_output, outputs, raw_response)


def deactivate_user_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    raw_response = client.deactivate_user(user_id)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }

    readable_output = f"### User {args.get('username')} deactivated"

    return (
        readable_output,
        outputs,
        raw_response,  # raw response - the original response
    )


def suspend_user_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    raw_response = client.suspend_user(user_id)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }

    readable_output = f"### {args.get('username')} status is Suspended"
    return (readable_output, outputs, raw_response)


def unsuspend_user_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    raw_response = client.unsuspend_user(user_id)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }

    readable_output = f"### {args.get('username')} is no longer SUSPENDED"
    return (readable_output, outputs, raw_response)


def get_user_factors_command(client, args):
    user_id = args.get("userId")

    if not (args.get("username") or user_id):
        raise Exception("You must supply either 'Username' or 'userId")

    if not user_id:
        user_id = client.get_user_id(args.get("username"))

    raw_response = client.get_user_factors(user_id)
    if not raw_response or len(raw_response) == 0:
        return "No Factors found"

    factors = client.get_readable_factors(raw_response)
    context = createContext(factors, removeNull=True)
    outputs = {
        "Account(val.ID && val.ID === obj.ID)": {"Factor": context, "ID": user_id},
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"Factors for user: {user_id}\n {tableToMarkdown('Factors', factors)}"
    return (readable_output, outputs, raw_response)


def reset_factor_command(client, args):
    factor_id = args.get("factorId")
    user_id = args.get("userId")

    if not (args.get("username") or user_id):
        raise Exception("You must supply either 'Username' or 'userId")

    if not user_id:
        user_id = client.get_user_id(args.get("username"))

    raw_response = client.reset_factor(user_id, factor_id)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"Factor: {factor_id} deleted"
    return (readable_output, outputs, raw_response)


def set_password_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    password = args.get("password")

    raw_response = client.set_password(user_id, password)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"{args.get('username')} password was last changed on {raw_response.get('passwordChanged')}"

    if argToBoolean(args.get("temporary_password", False)):
        expire_password_response = client.expire_password(user_id, args)
        expire_password_readable_output = tableToMarkdown("Okta Temporary Password", expire_password_response, removeNull=True)
        readable_output = f"{readable_output}\n{expire_password_readable_output}"

    return readable_output, outputs, raw_response


def expire_password_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    hide_password = argToBoolean(args.get("hide_password", False))
    revoke_session = argToBoolean(args.get("revoke_session", False))

    if not (args.get("username") or user_id):
        raise Exception("You must supply either 'Username' or 'userId")
    if revoke_session is True:
        raw_response = client.revoke_session(user_id)
    else:
        raw_response = client.expire_password(user_id, args)
    if "tempPassword" in raw_response and hide_password:
        raw_response["tempPassword"] = "Output removed by user. hide_password argument set to True"
    user_context = client.get_users_context(raw_response)

    readable_output = tableToMarkdown("Okta Expired Password", raw_response, removeNull=True)
    outputs = {
        "Account(val.ID && val.ID === obj.ID)": createContext(user_context, removeNull=True),
        "Okta.Metadata(true)": client.request_metadata,
    }

    return (readable_output, outputs, raw_response)


def add_user_to_group_command(client, args):
    group_id = args.get("groupId")
    user_id = args.get("userId")

    if (not (args.get("username") or user_id)) or (not (args.get("groupName") or group_id)):
        raise Exception("You must supply either 'Username' or 'userId")
    if not user_id:
        user_id = client.get_user_id(args.get("username"))
    if not group_id:
        group_id = client.get_group_id(args.get("groupName"))
        if group_id is None:
            raise ValueError("The group was not found.")
    raw_response = client.add_user_to_group(user_id, group_id)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"User: {user_id} added to group: {args.get('groupName')} successfully"
    return (readable_output, outputs, raw_response)


def remove_from_group_command(client, args):
    group_id = args.get("groupId")
    user_id = args.get("userId")

    if (not (args.get("username") or user_id)) or (not (args.get("groupName") or group_id)):
        raise Exception("You must supply either 'Username' or 'userId' and either 'groupName' or 'groupId'")
    if not user_id:
        user_id = client.get_user_id(args.get("username"))
    if not group_id:
        group_id = client.get_group_id(args.get("groupName"))
        if group_id is None:
            raise ValueError("The group was not found.")
    raw_response = client.remove_user_from_group(user_id, group_id)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"User: {user_id} was removed from group: {args.get('groupName')} successfully"
    return (readable_output, outputs, raw_response)


def get_groups_for_user_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    raw_response = client.get_groups_for_user(user_id)
    groups = client.get_readable_groups(raw_response)

    context = createContext(groups, removeNull=True)
    outputs = {
        "Account(val.ID && val.ID === obj.ID)": {"Group": context, "ID": user_id, "Type": "Okta"},
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"Okta groups for user: {args.get('username')}\n {tableToMarkdown('Groups', groups)}"

    return (readable_output, outputs, raw_response)


def verify_push_factor_command(client, args):
    user_id = args.get("userId")
    factor_id = args.get("factorId")
    polling_time = arg_to_number(args.get("polling_time", DEFAULT_POLLING_TIME))
    max_polling_calls = arg_to_number(args.get("max_polling_calls", DEFAULT_MAX_POLLING_CALLS))
    polling = argToBoolean(args.get("polling", True))

    raw_response = client.verify_push_factor(user_id, factor_id)
    poll_link = raw_response.get("_links").get("poll")
    if not poll_link:
        raise Exception("No poll link for the push factor challenge")
    if not polling:
        outputs = {"Okta.PollingStatusURL(true)": poll_link.get("href")}
        readable_output = (
            "Push factor challenge has been initiated. To check the push factor challenge status, "
            f"use the 'okta-verify-mfa-status' command with url: {poll_link.get('href')}"
        )
        return (readable_output, outputs, raw_response)

    poll_response = client.poll_verify_push(poll_link.get("href"), polling_time, max_polling_calls)

    outputs = {
        "Account(val.ID && val.ID === obj.ID)": {"ID": user_id, "VerifyPushResult": poll_response.get("factorResult")},
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"Verify push factor result for user {user_id}: {poll_response.get('factorResult')}"
    return (readable_output, outputs, raw_response)


def verify_mfa_status_command(client: Client, args: dict):
    """Checks the status of a push factor procedure

    Args:
        client (Client): The Okta client
        args (dict): The command arguments
    """
    polling_url = args.get("polling_url", "")
    user_id, factor_id = extract_user_and_factor_id_from_url(polling_url)

    raw_response = client.get_push_factor_status(polling_url)
    status = raw_response.get("factorResult")
    outputs = {
        "Okta.FactorResult(val.ID && val.ID === obj.ID)": {
            "ID": factor_id,
            "factorResult": status,
            "UserID": user_id,
        },
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"The status of the push factor challenge is {status}"
    return (readable_output, outputs, raw_response)


def search_command(client, args):
    term = args.get("term")
    limit = args.get("limit") or SEARCH_LIMIT
    verbose = args.get("verbose")
    advanced_search = args.get("advanced_search", "")
    if not term and not advanced_search:
        raise DemistoException("Please provide either the term or advanced_search argument")
    raw_response = client.search(term, limit, advanced_search)

    if raw_response and len(raw_response) > 0:
        users_context = client.get_users_context(raw_response)
        users_readable = client.get_readable_users(raw_response, verbose)
        context = createContext(users_context, removeNull=True)
        outputs = {
            "Account(val.ID && val.ID === obj.ID)": context,
            "Okta.Metadata(true)": client.request_metadata,
        }
        if verbose == "true":
            readable_output = f"### Okta users found:\n {users_readable}"
        else:
            readable_output = f"### Okta users found:\n {tableToMarkdown('Users:', users_readable)} "
        return (readable_output, outputs, raw_response)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }
    return "No users found in Okta", outputs, raw_response


def get_user_command(client: Client, args: dict):
    if not (args.get("username") or args.get("userId") or args.get("userEmail")):
        raise Exception("You must supply either 'Username' or 'userId' or 'userEmail'")

    raw_response = None
    if user_term := args.get("userEmail"):
        raw_response, _ = client.list_users({"filter": f'profile.email eq "{user_term}"', "limit": 1})
    else:
        user_term = args.get("userId") or args.get("username")
        try:
            raw_response = client.get_user(user_term)
        except Exception as e:
            if "404" not in str(e):
                raise e

    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return (f"User {user_term!r} was not found.", outputs, {})

    verbose = args.get("verbose")

    user_context = client.get_users_context(raw_response)
    user_readable = client.get_readable_users(raw_response, verbose)
    outputs = {
        "Account(val.ID && val.ID === obj.ID)": createContext(user_context),
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = user_readable if verbose == "true" else f"{tableToMarkdown(f'User:{user_term}', user_readable)} "
    return (readable_output, outputs, raw_response)


def create_user_command(client, args):
    group_ids = args.get("groupIds")
    cred = client.build_credentials(args)
    profile = client.build_profile(args)
    if group_ids:
        group_ids = args.get("groupIds").split(",")
    activate = "true" if args.get("activate") == "true" else "false"
    raw_response = client.create_user(cred, profile, group_ids, activate)
    user_context = client.get_users_context(raw_response)
    outputs = {
        "Account(val.ID && val.ID === obj.ID)": createContext(user_context),
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = tableToMarkdown(f"Okta User Created: {args.get('login')}:", client.get_readable_users(raw_response))
    return (readable_output, outputs, raw_response)


def update_user_command(client, args):
    user_id = client.get_user_id(args.get("username"))
    cred = client.build_credentials(args)
    profile = client.build_profile(args)
    profile["login"] = args.get("username")
    raw_response = client.update_user(user_id, profile, cred)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = tableToMarkdown(f"Okta user: {args.get('username')} Updated:", raw_response.get("profile"))
    return (readable_output, outputs, raw_response)


def get_group_members_command(client, args):
    if not (args.get("groupId") or args.get("groupName")):
        raise Exception("You must supply either 'groupName' or 'groupId")
    limit = args.get("limit")
    group_id = args.get("groupId") or client.get_group_id(args.get("groupName"))
    if group_id is None:
        raise ValueError("The group was not found.")
    raw_members = client.get_group_members(group_id, limit)
    users_context = client.get_users_context(raw_members)
    users_readable = client.get_readable_users(raw_members, args.get("verbose"))
    context = createContext(users_context, removeNull=True)
    outputs = {
        "Account(val.ID && val.ID === obj.ID)": context,
        "Okta.Metadata(true)": client.request_metadata,
    }
    if args.get("verbose") == "true":
        return (f"### Users for group: {args.get('groupName') or group_id}:\n {users_readable}", outputs, raw_members)
    return (tableToMarkdown(f"Users for group: {args.get('groupName') or group_id}", users_readable), outputs, raw_members)


def list_users_command(client, args):
    raw_response, after_tag = client.list_users(args)
    verbose = args.get("verbose")
    users = client.get_readable_users(raw_response, verbose)
    user_context = client.get_users_context(raw_response)
    context = createContext(user_context, removeNull=True)
    if verbose == "true":
        readable_output = f"### Okta users found:\n {users}"
    else:
        readable_output = f"### Okta users found:\n {tableToMarkdown('Users', users)} "
    if after_tag:
        readable_output += f"\n### tag: {after_tag}"
    outputs = {
        "Account(val.ID && val.ID == obj.ID)": context,
        "Okta.User(val.tag)": {"tag": after_tag},
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def list_groups_command(client, args):
    raw_response = client.list_groups(args)
    groups = client.get_readable_groups(raw_response)
    context = createContext(groups, removeNull=True)
    outputs = {
        "Okta.Group(val.ID && val.ID === obj.ID)": context,
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = tableToMarkdown("Groups", groups)

    return (readable_output, outputs, raw_response)


def get_logs_command(client, args):
    raw_response = client.get_logs(args)
    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return "No logs found", outputs, raw_response

    logs = client.get_readable_logs(raw_response)
    readable_output = tableToMarkdown("Okta Events", logs)
    outputs = {
        "Okta.Logs.Events(val.uuid && val.uuid === obj.uuid)": createContext(raw_response),
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def get_failed_login_command(client, args):
    args["filter"] = 'eventType eq "user.session.start" and outcome.result eq "FAILURE"'
    raw_response = client.get_logs(args)
    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return "No logs found", outputs, raw_response
    logs = client.get_readable_logs(raw_response)
    readable_output = tableToMarkdown("Failed Login Events", logs)
    outputs = {
        "Okta.Logs.Events(val.uuid && val.uuid === obj.uuid)": createContext(raw_response),
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def get_group_assignments_command(client, args):
    args["filter"] = 'eventType eq "group.user_membership.add"'
    raw_response = client.get_logs(args)
    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return "No logs found", outputs, raw_response
    logs = client.get_readable_logs(raw_response)
    readable_output = tableToMarkdown("Group Assignment Events", logs)
    outputs = {
        "Okta.Logs.Events(val.uuid && val.uuid === obj.uuid)": createContext(raw_response),
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def get_application_assignments_command(client, args):
    args["filter"] = 'eventType eq "application.user_membership.add"'
    raw_response = client.get_logs(args)
    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return "No logs found", outputs, raw_response
    logs = client.get_readable_logs(raw_response)
    readable_output = tableToMarkdown("Application Assignment Events", logs)
    outputs = {
        "Okta.Logs.Events(val.uuid && val.uuid === obj.uuid)": createContext(raw_response),
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def get_application_authentication_command(client, args):
    args["filter"] = 'eventType eq "user.authentication.sso"'
    raw_response = client.get_logs(args)
    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return "No logs found", outputs, raw_response
    logs = client.get_readable_logs(raw_response)
    readable_output = tableToMarkdown("Application Authentication Events", logs)
    outputs = {
        "Okta.Logs.Events(val.uuid && val.uuid === obj.uuid)": createContext(raw_response),
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def delete_user_command(client, args):
    if not (args.get("username") or args.get("userId")):
        raise Exception("You must supply either 'Username' or 'userId")
    user_term = args.get("userId") or args.get("username")
    # Deletes a user permanently. This operation can only be performed on users that have a DEPROVISIONED status.
    # This action cannot be recovered!This operation on a user that hasn't been deactivated
    # causes that user to be deactivated. A second delete operation is required to delete the user.
    user = client.get_user(user_term)
    if user.get("status") != "DEPROVISIONED":
        client.deactivate_user(args.get("userId") or client.get_user_id(args.get("username")))
    raw_response = client.delete_user(user_term)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"User: {user_term} was Deleted successfully"
    return (readable_output, outputs, raw_response)


def clear_user_sessions_command(client, args):
    user_id = args.get("userId")
    revoke_oauth_tokens = argToBoolean(args.get("revokeOauthTokens", True))
    raw_response = client.clear_user_sessions(user_id, revoke_oauth_tokens)
    outputs = {
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"### User session was cleared for: {user_id}"

    return readable_output, outputs, raw_response


def get_zone_command(client, args):
    raw_response = client.get_zone(args.get("zoneID", ""))
    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return "No zones found.", outputs, raw_response
    readable_output = tableToMarkdown(
        "Okta Zones", raw_response, headers=["name", "id", "gateways", "status", "system", "lastUpdated", "created"]
    )
    outputs = {
        "Okta.Zone(val.id && val.id === obj.id)": createContext(raw_response),
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def list_zones_command(client, args):
    raw_response = client.list_zones(args.get("limit"))
    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return "No zones found.", outputs, raw_response
    readable_output = tableToMarkdown(
        "Okta Zones", raw_response, headers=["name", "id", "gateways", "status", "system", "lastUpdated", "created"]
    )
    outputs = {
        "Okta.Zone(val.id && val.id === obj.id)": createContext(raw_response),
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def apply_zone_updates(zoneObject, zoneName, gatewayIPs, proxyIPs, updateType="OVERRIDE"):
    # If user provided a new zone name - set it
    if zoneName:
        zoneObject["name"] = zoneName

    gateways = []
    proxies = []
    existing_gateways: list = zoneObject.get("gateways")
    existing_proxies: list = zoneObject.get("proxies")

    if gatewayIPs:
        for ip in gatewayIPs:
            if "-" in ip:  # Check for IP range notation
                gateways.append({"type": "RANGE", "value": ip})
            else:  # If not a range, treat it as a single IP
                cidr_value = f"{ip}/32" if "/" not in ip else f"{ip}"
                gateways.append({"type": "CIDR", "value": cidr_value})

        if existing_gateways is not None and updateType == "APPEND":
            zoneObject["gateways"] = existing_gateways + gateways
        else:
            zoneObject["gateways"] = gateways

    if proxyIPs:
        for ip in proxyIPs:
            if "-" in ip:  # Check for IP range notation
                proxies.append({"type": "RANGE", "value": ip})
            else:  # If not a range, treat it as a single IP
                cidr_value = f"{ip}/32" if "/" not in ip else f"{ip}"
                proxies.append({"type": "CIDR", "value": cidr_value})

        if existing_proxies is not None and updateType == "APPEND":
            zoneObject["proxies"] = existing_proxies + proxies
        else:
            zoneObject["proxies"] = proxies

    return zoneObject


def create_zone_command(client, args):
    zone_name = args.get("name")
    gateway_ips = argToList(args.get("gateway_ips"))
    proxies = argToList(args.get("proxies"))
    if not (gateway_ips or proxies):
        raise Exception("You must supply either 'gateway_ips' or 'proxies'.")

    zoneObject = {"name": "", "type": "IP", "status": "ACTIVE", "gateways": [], "proxies": []}
    zoneObject = apply_zone_updates(zoneObject, zone_name, gateway_ips, proxies)

    raw_response = client.create_zone(zoneObject)
    if not raw_response:
        outputs = {
            "Okta.Metadata(true)": client.request_metadata,
        }
        return "Zone not created.", outputs, raw_response
    readable_output = tableToMarkdown(
        "Okta Zones", raw_response, headers=["name", "id", "gateways", "status", "system", "lastUpdated", "created"]
    )
    outputs = {
        "Okta.Zone(val.id && val.id === obj.id)": createContext(raw_response),
        "Okta.Metadata(true)": client.request_metadata,
    }
    return (readable_output, outputs, raw_response)


def update_zone_command(client, args):
    if not args.get("zoneName", "") and not args.get("gatewayIPs", "") and not args.get("proxyIPs", ""):
        return ("Nothing to update", {}, "Nothing to update")
    zoneID = args.get("zoneID", "")
    updateType = args.get("updateType")
    zoneObject = client.get_zone(zoneID)
    if zoneID == zoneObject.get("id"):
        zoneName = args.get("zoneName", "")
        gatewayIPs = argToList(args.get("gatewayIPs", ""))
        proxyIPs = argToList(args.get("proxyIPs", ""))
        zoneObject = apply_zone_updates(zoneObject, zoneName, gatewayIPs, proxyIPs, updateType)

        raw_response = client.update_zone(zoneObject)
        if not raw_response:
            outputs = {
                "Okta.Metadata(true)": client.request_metadata,
            }
            return "Got empty response.", outputs, raw_response

        readable_output = tableToMarkdown(
            "Okta Zones", raw_response, headers=["name", "id", "gateways", "status", "system", "lastUpdated", "created"]
        )
        outputs = {
            "Okta.Zone(val.id && val.id === obj.id)": createContext(raw_response),
            "Okta.Metadata(true)": client.request_metadata,
        }
        return (readable_output, outputs, raw_response)
    else:
        return "No zone found in Okta with this ID.", {}, {}


def assign_group_to_app_command(client, args):
    group_id = args.get("groupId")
    if not group_id:
        group_id = client.get_group_id(args.get("groupName"))
        if group_id is None:
            raise ValueError("The group was not found.")
    app_id = client.get_app_id(args.get("appName"))
    raw_response = client.assign_group_to_app(group_id, app_id)
    outputs = {"Okta.Metadata(true)": client.request_metadata}
    readable_output = f"Group: {args.get('groupName')} added to PA App successfully"
    return (readable_output, outputs, raw_response)


def create_group_command(client, args):
    profile = client.build_group_profile(args)
    raw_response = client.create_group(profile)
    group_context = client.get_groups_context(raw_response)
    outputs = {
        "OktaGroup(val.ID && val.ID === obj.ID)": createContext(group_context),
        "Okta.Metadata(true)": client.request_metadata,
    }
    readable_output = f"Group Created: [GroupID:{raw_response['id']}, GroupName: {raw_response['profile']['name']}]"
    return (readable_output, outputs, raw_response)


def reset_auth_command(client, args):
    reset_integration_context()
    return CommandResults(readable_output="Authentication data cleared successfully.")


def get_after_tag(url):
    """retrieve the after param from the url

    Args:
        url: some url

    Returns:
        String: the value of the 'after' query param.
    """
    parsed_url = urlparse(url)
    captured_value = parse_qs(parsed_url.query)["after"][0]
    return captured_value


def delete_limit_param(url):
    """Delete the limit param from the url

    Args:
        url: some url

    Returns:
        String: the url with the limit query param.
    """
    parsed_url = urlparse(url)
    query_dict = parse_qs(parsed_url.query)
    query_dict.pop("limit")
    return urlunparse(parsed_url._replace(query=urlencode(query_dict, True)))


def extract_user_and_factor_id_from_url(url: str) -> tuple[str, str]:
    """Extracts the user ID and Factor ID from the polling url.

    Args:
        url (str): The polling url.

    Returns:
        tuple[str, str]: The user ID and Factor ID.
    """
    parsed_url = urlparse(url)
    path_parts = parsed_url.path.split("/")
    # Example for url: https://example.okta.com/api/v1/users/user-id/factors/factor-id
    # path_parts: ['', 'api', 'v1', 'users', 'user-id', 'factors', 'factor-id']
    try:
        user_id = path_parts[path_parts.index("users") + 1]
        factor_id = path_parts[path_parts.index("factors") + 1]
        return user_id, factor_id
    except (ValueError, IndexError):
        raise DemistoException(f"Could not extract user ID and Factor ID from the polling URL: {url}")


def main():
    try:
        params = demisto.params()

        demisto.debug(f"Command being called is {demisto.command()}")
        commands = {
            "test-module": module_test,
            "okta-unlock-user": unlock_user_command,
            "okta-deactivate-user": deactivate_user_command,
            "okta-activate-user": activate_user_command,
            "okta-suspend-user": suspend_user_command,
            "okta-unsuspend-user": unsuspend_user_command,
            "okta-reset-factor": reset_factor_command,
            "okta-set-password": set_password_command,
            "okta-expire-password": expire_password_command,
            "okta-add-to-group": add_user_to_group_command,
            "okta-remove-from-group": remove_from_group_command,
            "okta-get-groups": get_groups_for_user_command,
            "okta-get-user-factors": get_user_factors_command,
            "okta-verify-push-factor": verify_push_factor_command,
            "okta-verify-mfa-status": verify_mfa_status_command,
            "okta-search": search_command,
            "okta-get-user": get_user_command,
            "okta-create-user": create_user_command,
            "okta-update-user": update_user_command,
            "okta-get-group-members": get_group_members_command,
            "okta-list-users": list_users_command,
            "okta-list-groups": list_groups_command,
            "okta-get-logs": get_logs_command,
            "okta-get-failed-logins": get_failed_login_command,
            "okta-get-application-assignments": get_application_assignments_command,
            "okta-get-group-assignments": get_group_assignments_command,
            "okta-get-application-authentication": get_application_authentication_command,
            "okta-delete-user": delete_user_command,
            "okta-clear-user-sessions": clear_user_sessions_command,
            "okta-list-zones": list_zones_command,
            "okta-get-zone": get_zone_command,
            "okta-update-zone": update_zone_command,
            "okta-create-zone": create_zone_command,
            "okta-create-group": create_group_command,
            "okta-assign-group-to-app": assign_group_to_app_command,
            "okta-auth-reset": reset_auth_command,
        }

        command = demisto.command()

        client = Client(
            base_url=params["url"].rstrip("/"),
            verify=(not params.get("insecure", False)),
            headers={
                "Accept": "application/json",
                "Content-Type": "application/json",
            },
            proxy=params.get("proxy", False),
            ok_codes=(200, 201, 204),
            api_token=params.get("credentials", {}).get("password") or params.get("apitoken"),
            auth_type=AuthType.OAUTH if argToBoolean(params.get("use_oauth", False)) else AuthType.API_TOKEN,
            client_id=params.get("client_id"),
            scopes=OAUTH_TOKEN_SCOPES,
            private_key=params.get("private_key"),
            jwt_algorithm=JWTAlgorithm(params["jwt_algorithm"]) if params.get("jwt_algorithm") else None,
            key_id=params.get("key_id", None),
        )

        if command in commands:
            result = commands[command](client, demisto.args())

            if isinstance(result, CommandResults | str | dict):
                return_results(result)

            else:
                human_readable, outputs, raw_response = result
                return_outputs(readable_output=human_readable, outputs=outputs, raw_response=raw_response)

        else:
            raise NotImplementedError(f"Command {command} is not implemented.")

    except Exception as e:
        return_error(f"Failed to execute {demisto.command()} command. Error: {e!s}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()