Send Notifications for a Breached Password Event with Okta Workflows

This article contains a sample workflow to notify users when their credentials have appeared in a list of breached credentials. It also serves as a useful reference guide for using event hooks alongside Okta Workflows.

Overview

This template provides a sample workflow to notify users when their credentials have appeared in a list of breached credentials. The Okta System Log records this as a Breached Password event.

The template uses an event hook to trigger an API endpoint and start the flow. Then it composes a notification message and sends it to your user through the Gmail and Slack connectors.

This template also serves as a useful reference guide for using event hooks alongside Okta Workflows. There are several scenarios where you may want to consider using event hooks rather than one of the built-in Okta connectors:

  • If an event-hook eligible event is available but not yet built into a connector
  • If a use case would benefit from Event Hook Filtering, a feature that isn’t currently available in an Okta connector.

Prerequisites

Before you get started, here are the things you need:

  • Access to an Okta tenant with Okta Workflows enabled for your org
  • Authorized connectors for Slack and Gmail (or any other notification mechanism)

Setup Steps

Import and prepare the flows

The flows described in this article can be downloaded from: https://iamse.blog/download/send-notifications-for-a-breached-password-event-with-okta-workflows/
You can then create a new folder in Workflows and import the file.

  • The connectors in both flows must have valid connections.
    • For the Close card in the Event Initiated Flow, you can use a No Auth type connection.
  • Select a Slack channel in the If Error section of the Process event object flow.
  • After making any necessary changes, save both flows.
  • Optional. To keep the data that passes through your flow, enable the Save all data that passes through the Flow? option. This data is helpful for the design and troubleshooting stages.
  • In the Event Initiated Flow, click the Endpoint settings </> icon at the bottom of the API Endpoint card.
    • Copy the Invoke URL. You need this value to create the event hook.

Create the Event Hook in the Admin Console

  1. Open your Okta Admin Console and go to Workflow > Event Hooks.
  2. Click Create event hook.
  3. In the Add hook details dialog, fill in the following fields:
    1. Name: Give your event hook a memorable name, for example Breached Passwords Notification. You can optionally provide a description as well.
    2. URL: Paste in the Invoke URL that you copied for the API endpoint.
    3. In the Subscribe to events field, type A credential, and then select the event named A credential, such as a password, which is associated with a known breach was used during an authentication flow.
    4. Click Save & Continue.
    5. Continue to proceed and activate the hook.

Assuming the flows and hook are activated, everything is set up and the flow executes when a security.breached_credential.detected event occurs.

Optionally, you can modify or extend the Process event object flow for various other use cases.

  • Change what the notifications say, who they’re sent to, or what systems they’re sent from.
  • Create tickets in downstream systems
  • Add a user to an Okta group that is subject to more stringent policies

This template also serves as a useful starting point when you need to work with Okta Event Hooks.

Testing this Flow

The only way that this flow can be invoked is when a security.breached_credential.detected event occurs and fires the associated event hook.

As the breached credential detection process is fully automated, there’s no way to manually trigger one of these events. Instead, you can use a preview of the payload to test the flow.

Note: It’s possible to open the previously configured event hook and click Deliver Request on the Preview tab. This sends a sample event to the Workflows API endpoint.

Using a payload preview is a good way to test event hook flows in many cases. However, without an existing security.breached_credential.detected event in the System Log, the sample event preview lacks some necessary data – such as an actor object that is populated with data – which this Workflow template attempts to parse values from.

Of course, when a real event occurs in your tenant, the payload will include the full set of data that these flows need; in our case, for testing purposes, we can manually trigger the flows using some sample data.

The following steps explain how to alter the JSON sample provided below to manually test your flow.

  1. Copy the sample JSON into a text editor and modify the following values:
    • actor.displayName
      • This is the user’s name for the notification message
      • Currently this is set as Test User
    • actor.alternateId
      • This is the email address where the message is sent
      • The flow also uses this to look up the Slack User ID
      • Currently this is set as test@example.com
    • displayMessage
      • This is the email subject line
  2. Open the Event Initiated Flow in Workflows
  3. Click Run and in the body input, paste the modified JSON text. Then click Run again.

Sample JSON

Click here for Sample JSON
{
        "eventType": "com.okta.event_hook",
        "eventTypeVersion": "1.0",
        "cloudEventsVersion": "0.1",
        "source": "https://vl2.okta1.com/api/v1/eventHooks/example",
        "eventId": "example",
        "data": {
            "events": [
                {
                    "uuid": "exampleID1234",
                    "published": "2024-06-17T22:00:56.423Z",
                    "eventType": "security.breached_credential.detected",
                    "version": "0",
                    "displayMessage": "test@example.com - Credential set matches to known compromised credentials.",
                    "severity": "WARN",
                    "client": {
                        "userAgent": {
                            "rawUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
                            "os": "Mac OS X",
                            "browser": "CHROME"
                        },
                        "zone": "null",
                        "device": "Computer",
                        "id": null,
                        "ipAddress": "555.555.55.5",
                        "geographicalContext": {
                            "city": null,
                            "state": null,
                            "country": null,
                            "postalCode": null,
                            "geolocation": {
                                "lat": 55.55,
                                "lon": -111.55
                            }
                        },
                        "ipChain": [
                            {
                                "ip": "555.555.55.5",
                                "geographicalContext": {
                                    "city": null,
                                    "state": null,
                                    "country": null,
                                    "postalCode": null,
                                    "geolocation": {
                                        "lat": 55.55,
                                        "lon": -111.55
                                    }
                                },
                                "version": "V4",
                                "source": null
                            }
                        ]
                    },
                    "device": null,
                    "actor": {
                        "id": "exampleOktaID12345",
                        "type": "User",
                        "alternateId": "test@example.com",
                        "displayName": "Test User",
                        "detailEntry": null
                    },
                    "outcome": {
                        "result": "SUCCESS",
                        "reason": null
                    },
                    "target": [
                        {
                            "id": "exampleID1234",
                            "type": "AuthenticatorEnrollment",
                            "alternateId": "unknown",
                            "displayName": "Password",
                            "detailEntry": null
                        }
                    ],
                    "transaction": {
                        "type": "WEB",
                        "id": "exampleID1234",
                        "detail": {}
                    },
                    "debugContext": {
                        "debugData": {
                            "authnRequestId": "exampleID1234",
                            "deviceFingerprint": "exampleFingerprint1234",
                            "behaviors": "{New Geo-Location=UNKNOWN, New Device=UNKNOWN, New IP=UNKNOWN, New State=BAD_REQUEST, New Country=BAD_REQUEST, Velocity=UNKNOWN, New City=BAD_REQUEST}",
                            "requestId": "exampleID1234",
                            "dtHash": "exampleHash1234",
                            "risk": "{reasons=Anomalous Location, Anomalous Device, level=HIGH}",
                            "requestUri": "/idp/idx/challenge/answer",
                            "targetEventHookIds": "exampleID1234",
                            "url": "/idp/idx/challenge/answer?"
                        }
                    },
                    "legacyEventType": null,
                    "authenticationContext": {
                        "authenticationProvider": null,
                        "credentialProvider": null,
                        "credentialType": null,
                        "issuer": null,
                        "authenticationStep": 0,
                        "rootSessionId": "exampleID1234",
                        "externalSessionId": "exampleID1234",
                        "authenticatorContext": null,
                        "interface": null
                    },
                    "securityContext": {
                        "asNumber": null,
                        "asOrg": null,
                        "isp": null,
                        "domain": null,
                        "isProxy": null
                    },
                    "insertionTimestamp": null
                }
            ]
        },
        "eventTime": "2024-06-17T22:01:21.809026Z",
        "contentType": "application/json"
    }

How it works

In the Event Initiated Flow:

  • API Connector – Close is used to return a 200 status code to the Okta Event Hooks system. Acknowledging receipt of an event is helpful because it tells the system it doesn’t need to redeliver the event.
  • Object – Get Multiple is used to get the data.events list of objects so we can iterate over it.

Finally, in the Process event object flow:

  • Another Object – Get Multiple is used to parse values out of the security.breached_credential.detected event payload.
  • Text – Compose is used to build the notification message.
  • The Gmail and Slack connectors are leveraged to send notifications and lookup a user.
    • Those actions are placed in the “Try” block of an If Error function, which allows us to perform an alternate set of actions in the “If Error” block if one of them should fail.
    • Building error handling like this into flows is a good way to increase their reliability and resiliency, and can alert your team if there is something that needs attention.

Screenshots

Close the HTTP connection and get the data.events list from the payload, then iterate over it.
Close the HTTP connection, get the data.events list from the payload, then iterate over it.
Get values from event object, create message, send messages.
Get values from event object, create message, send messages.
Example of the Slack message sent to the end-user. They will also receive a similar email.
Example of the Slack message sent to the end-user. They will also receive a similar email.

Limitations & Known Issues

No known issues, please refer to:

One thought on “Send Notifications for a Breached Password Event with Okta Workflows

Leave a Reply