Okta Workflows How-To: Use Workflows for Inline Hooks
Okta Workflows is a no-code platform for automating identity processes.
This blog post is based on questions asked during office hours or the #okta-workflows channel in MacAdmins Slack.
Important
Low-latency flows offer an alternative to existing inline hook custom endpoint solutions. Depending on the use case, low-latency flows may not always be a suitable way to implement the hook endpoint. A suitable amount of testing will be required to establish if the endpoint processing latency is within an acceptable range. Read about the criteria for low-latency flows.
Getting started
With this release of low-latency flows, Okta has laid the groundwork for supporting any web hook that is time sensitive. Traditionally, organizations have to spend engineering effort to build, deploy, host, and manage their own system in order to enrich a user’s profile (token or session), or notify/remediate a security threat. Those systems become even more expensive when they are powering synchronous use cases that require high availability, reliability and security. Now customers can confidently rely on Okta Workflows to power these use cases. As an example, this article will show you how Okta Workflows can be used to enrich a token by mediating an Okta Inline hook with an external API.
The Low-latency feature ensures consistent execution times for your flows by running flows that meet a specific set of criteria separately from those that don’t. Okta routes low-latency flows to an optimized processing queue with minimal wait times and increased resource availability. These flows execute at a higher velocity compared to flows that run in standard mode or flows that get throttled. For more information on Low Latency Flows, see this page: Low Latency Flows
Inline hooks are outbound calls from Okta to your own custom code, triggered at specific points in Okta process flows. They allow you to integrate custom functionality into those flows. Inline hooks use synchronous calls, which means that the Okta process that triggered the hook is paused until a response from your service is received.
The current available Inline Hooks are:
Name |
Description |
Customizes tokens returned by Okta API Access Management |
|
Adds custom logic to the user import process |
|
Customizes SAML assertions returned by Okta |
|
Customizes handling of Self-Service Registration (SSR) and Progressive Enrollment support |
|
Verifies a user-supplied password to support the migration of users to Okta |
|
Customizes Okta’s flows that send SMS or Voice messages |
Note: When Okta calls an external service, it enforces a default timeout of 3 seconds. Okta will attempt at most one retry. A request isn’t retried if the external service endpoint responds with a 2xx HTTP success code or a 4xx HTTP error code. If the external service endpoint responds with a redirect, it isn’t followed. The default timeout of 3 seconds can be altered by contacting Okta support.
See this page for more information on Inline Hooks: Inline hooks | Okta Developer
Token Inline Hook
As an example, I’m going to use a Token Inline Hook to demonstrate how Okta Workflows can be used to implement the hook endpoint. This type of inline hook is triggered when OAuth 2.0 and OpenID Connect (OIDC) tokens are minted by your Okta Custom Authorization Server. Before sending the token to the requester, Okta calls out to your external service, and your service can respond with commands to add custom claims to the token or to modify existing claims. This functionality can be used to add data that is sensitive, calculated at runtime, or complexly-structured and not appropriate for storing in Okta user profiles. Data added this way is never logged or stored by Okta. As an example, tokens minted for a medical app could be augmented with confidential patient data provided by your external service and not stored in Okta.
For more information on Token Inline Hooks, see this page: Token inline hook reference | Okta Developer
This architecture is depicted by the following diagram:
Token Inline Hook processing flow via Okta Workflows
The sequence of the above diagram is as follows:
- User authenticates against Okta.
- The authentication process generates a token inline hook that calls an exposed endpoint in Okta Workflows. Workflows extracts the user identifier from the payload.
- Okta Workflows formats request end sends to external service.
- External service response includes user data.
- Okta Workflows extract the data from the payload and formats and sends the required response to Okta.
- Okta adds the data to the token, which is sent to the client.
Working Example
Step 1 – External Service
As an example external service, I just exposed a AWS DynamoDB table as an external service with the use of Lambda and the AWS API Gateway. You may have your own API that you would like to leverage. If not, this provides a quick and easy way to get an endpoint up and running.
The following diagram depicts the AWS architecture:
AWS Sample API Endpoint
See the documentation here: Tutorial: Build a CRUD API with Lambda and DynamoDB – Amazon API Gateway
To make the use case more meaningful, I replaced the Price and Name in the example with Patient Id. Here is my updated Lambda function code for the PUT operation:
case "PUT /items": let requestJSON = JSON.parse(event.body); await dynamo.send( new PutCommand({ TableName: tableName, Item: { id: requestJSON.id, patient_id: requestJSON.patient_id, }, }) ); body = `Put item ${requestJSON.id}`; break;
I then added a few records the the DynamoDB table for testing.
Here is some sample JSON for my PUT operation:
{ "id": "[email protected]", "patient_id": "421-1068" }
After adding a few records, here is my table contents:
Sample DynamoDB data
Step 2 – Create Okta Workflow
In this step, we are going to create a workflow that will process the event generated by a token inline hook.
Via the Okta Workflows console, create a new flow of type API Endpoint.
Flow type of API Endpoint
Then on the API Endpoint settings, select Expose as Webhook under Security Level. You can also select Expose as Public Service, but in this instance, you will need to implement the security yourself. Expose as Webhook is much easier as it looks after the security for you.
Expose API as Webhook
Once the flow has been created, add an Object Get card to the flow and extract the user identifier. If your external service is using the same identifier as the Okta username, then you can extract the username from:
data.context.user.profile.login
Alternatively, if your external service is using another attribute from the users Okta profile to identifier the user, then you can retrieve any custom claim from:
data.identity.claims.custon_claim
Note: Okta provides the ability to extend the users profile to support an unlimited number of custom attributes. These attributes can act as external identifiers and can be included as custom claims within the token.
Next, add a Text Concatenate or Text Compose card and append the user identifier onto the service URL.
Flow Components – Part 1
Then add an Object Construct card and format your header. In this case, all we need to send is a Context-Type of application/json. Then using a API Connector Raw Request card, make a GET request to your service endpoint.
Flow Components – Part 2
Then using an Object Get or Object Get Multiple card, extract the required data from the GET response.
Then using a Text Compose card, format the required JSON to add the respective data as a claim within the token. This formatted JSON is then returned by the flow as the response body.
Flow Components – Part 3
Here is my formatted JSON response:
{ "commands": [ { "type": "com.okta.access.patch", "value": [ { "op": "add", "path": "/claims/extPatientId", "value": "patient_id" } ] } ] }
Here are the two types of patch commands that can be used:
Command |
Description |
com.okta.identity.patch |
Modify an ID token. |
com.okta.access.patch |
Modify an access token. |
Patch Command Types
See the documentation here for a more detailed explanation: Token Hook Sample Payload
Finally we need to obtain the service endpoint of our completed workflow. Save and exit the flow. Within the respective flow folder, select the gear icon at the end of the flow and then select API Access.
API Access Configuration
This will bring up the following dialogue box:
API Endpoint Settings
Copy the Invoke URL, Alias and Client Token as these will be used in the next step.
The workflow illustrated above, can be downloaded from here: Workflow Example
You can also see a quick video walkthrough of the workflow here: Okta Workflows for Inline Hooks
Step 3 – Okta Configuration
In this step, we are going to create a Token Inline Hook via the Okta Administration console and then add that hook to a custom Authorization Server.
Create Inline Hook
Within the Okta Administration console, go to Workflow > Inline Hooks and click Add Inline Hook.
- Then select Token as the hook type.
- Give your Inline Hook a meaningful name
- Add your workflows Invoke URL as the inline hooks URL.
- Then add your workflow Alias as the Authentication Field and the Client Token as the Authentication Secret.
- Click Save
The Token Inline Hook is now ready to be called from your Custom Authorization Server.
Note: The Inline Hook has the ability to test the endpoint via the Preview Tab. It is recommended that you run a test at this point to ensure you have configured the Inline Hook correctly and that your Workflow is successfully being called.
Add Hook to Authorization Server
Within the Okta Administration console, go to Security > API and select your Custom Authorization Server that will be used to generate Access Tokens.
- Then go to the Access Policies tab and select the respective access policy that will be used for your client.
- Open the respective Rule that will be used and update the rule to use your configured Inline Hook configured in the previous step.
- Click Update Rule.
Select Inline Hook from Access Policy Rule
Test Inline Hook and Workflow
After authenticating, the resulting Access Token was the following:
{ "ver": 1, "jti": "AT.8grogxQ6aBsMOXciKYrV3asD25N7gMQ7IGk4_8ExIu4", "iss": "https://ms2.okta.com/oauth2/aus3xeww2mifknR4R697", "aud": "okta.com", "iat": 1675053080, "exp": 1675056680, "cid": "0oahz9yzbsyBfiX8H696", "uid": "00u72q61eF94eZudN696", "scp": [ "openid" ], "auth_time": 1675052990, "sub": "[email protected]", "extPatientId": "385-9756" }
As you can see, the external Patient Id was added as an extra claim within the token.
Looking at my workflow history, I can see that all executions ran in the Low Latency Queue and the execution time was sub-second which is generally acceptable for inline hooks.
Flow Execution Times
To learn more about low-latency flows and detailed eligibility criteria:
Get your Workflows questions answered
Do you have a question about Okta Workflows? Not sure how to build a flow? Join the weekly community office hours to get help.