Implementing A Request Response Type Trigger in Workflows

At the time of this writing, SailPoint Workflows does not have native support for our request response type triggers, such as the Access Request Submitted trigger. However, there is a way to implement request response triggers using the current capabilities of Workflows. This guide will demonstrate how to configure a Workflow that can successfully receive events from the Access Request Submitted trigger as well as respond with the decision to approve or deny the request.

This process will require two workflows: a workflow that is triggered by an external trigger, and a workflow that periodically updates the access token needed by the trigger subscription.

Workflow files

Attached are the two workflow scripts used in this guide. You only need the first file if you are ok with not rotating the token. If you would prefer to rotate the token on a schedule, then you will need the second workflow file as well. Feel free to upload these into your tenant as a starting point to help you follow along with the rest of this guide.

AccessRequestPreapproval20230413 (1).json (1.2 KB)
UpdateAccessRequestPreapprovalAccessToken20230418 (2).json (2.3 KB)

Create a workflow with the external trigger

Start by creating a new workflow that is triggered by an external trigger. Click the “New Access Token” button to get the client credentials used to authenticate to the workflow. Be sure to copy the client secret somewhere safe. You won’t be able to retrieve the secret once you close the window. You should see the following screen once you have generated the credentials.

Create an access token

The trigger subscription will need an access token in order to invoke the workflow. We need to generate that access token now before we can create the subscription. The following example uses cURL to generate the token, but you can also configure your Postman to do the same. Just be sure to replace the tenant, clientId and clientSecret with your specific values.

curl --request POST --location 'https://{tenant}.api.identitynow.com/oauth/token?grant_type=client_credentials&client_id=862aa98b-f39b-490c-xxxxxxxxxxxx&client_secret=6ebf9b15fbe73ab9670526c6db4xxxxxxxxxxxxxxxxxxxx' 

This will produce an access_token that expires in 68 years. The token itself is specifically limited and purpose built for triggering the workflow and that workflow alone. The token cannot be used for anything else. Therefore, it is low risk to have the timeout far into the future to prevent failed workflows. This allows admins to control token rotation on their schedule. This is important because once the token is rotated/regenerated, the former token is invalidated. Thus the admins control the rotation on their schedule and invalidate the tokens when a new one is generated avoiding failed workflow executions.

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRfaWQiOiIyZjlkOTZiMC03ZWIwLTRlNGUtYWE0MS03Nzk2YmU1ODQ2Y2YiLCJpbnRlcm5hbCI6ZmFsc2UsInBvZCI6InN0ZzAzLXVzZWFzdDEiLCJvcmciOiJkZXZyZWwiLCJhdXRob3JpdGllcyI6WyJBUEkiXSwiZW5hYmxlZCI6dHJ1ZSwiY2xpZW50X2lkIjoiODYyYWE5OGItZjM5Yi00OTBjLTk3YWYtNDlkMjNhZDE4MTAyIiwiYWNjZXNzVHlwZSI6Ik9GRkxJTkUiLCJzdHJvbmdfYXV0aF9zdXBwb3J0ZWQiOmZhbHNlLCJjbGFpbXNfcxxxxxx",
    "token_type": "bearer",
    "expires_in": 2147483646,
    "scope": "sp:workflow-execute:external sp:scopes:default",
    "accessType": "OFFLINE",
    "tenant_id": "2f9d96b0-7eb0-4e4e-aa41-xxxx",
    "internal": false,
    "pod": "stg03-useast1",
    "strong_auth_supported": false,
    "org": "devrel",
    "claims_supported": false,
    "enabled": true,
    "jti": "hlwbv15swTeSYpFLXGpl6HXUNNY"
}

Create a trigger subscription

Next, we need to subscribe to the Access Request Submitted event trigger (AKA Access Request Submitted). Navigate to the Event Trigger tab in the IDN UI and subscribe to the trigger.

  • Paste the “Client URL” you received when configuring the external trigger into the “Integration URL” of the subscription
  • Select a “Response Type” of “Async”
  • Set the “Response Deadline” to a value of your choice. For example, if you need an hour to respond, then set the deadline to PT1H
  • Set “Authentication Type” to “Bearer Token”. Set the value to the access_token that you generated in the last step
  • Leave the filter empty

Configure the workflow to respond to the event

A request response type trigger requires the subscriber to respond to the event in order to perform some action in IdentityNow. You can read more about this process here: https://developer.sailpoint.com/idn/docs/event-triggers/responding-request-response-trigger.

Let’s revisit the workflow and add an “HTTP Request” action that will respond to the event with a decision to approve/deny the request. For testing purposes, we will just automatically approve, but your final workflow will have more logic in place to determine whether to approve or deny.

  • Don’t select an “Authentication Type”. The event trigger will provide us a secret that we can pass in the body of the HTTP request.
  • The “Request URL” will be provided in the event payload. Select “Choose Variable” and set it to the following JSONpath variable: $.trigger._metadata.callbackURL
  • Set the “Method” to “POST”
  • Set the “Request Content Type” to “JSON”
  • We will use inline variables to configure the “Request Body”. Leave it as “Enter Value” and paste the following into the “Request Body”:
{
    "secret": "{{$.trigger._metadata.secret}}",
    "output": {
        "approved": true,
        "comment": "This access has passed workflow approval.",
        "approver": "Workflow"
    }
}


Finish your workflow with an end step operator.

image

Test the workflow

To test this workflow, we will save it and go back to the main workflow dashboard. Enable both the workflow and the trigger subscription.

With both enabled, let’s submit an access request. In my tenant, I will go to Request Center and request a role on behalf of someone else. Something to note is that this role I am requesting has no additional approval process, so it will show as “Approved” once the workflow responds. If you are requesting an access item that does have additional approver steps, it will still be in a pending state.

Once you submit the request, navigate to the “My Requests” tab and monitor the progress of the request. Depending on the load of your system, this can take several seconds/minutes to complete. Please refresh your page until you see that the request has been approved. Once it is approved, we can click on it and see that our workflow did receive and respond to the event because it passed the “Access Request Preliminary Processing” and we have its unique comment.

image

Configuring another workflow to update the trigger subscription access token

Recall that the access token we generated for the external trigger lasts for 68 years. This means your trigger subscription will start failing after 68 years have passed. In practice, you should not have to refresh this token, thereby requiring you to update the trigger subscription’s bearer token. You can skip this section and continue to “Final Steps”. However, in the event that an update to external triggers allows you to set a shorter access token duration, or you wish to rotate your credentials on more frequent schedule, the following steps will show you how to configure a second workflow to automatically refresh the token and update the trigger subscription. Just a reminder, if you are comfortable with the 68 year token expiration, then these steps are not necessary. Only follow these steps if you want to implement a more frequent token refresh schedule.

Get the access token

Start by creating a new workflow. Name it “Update Access Request Submitted Access Token”. This workflow will use a scheduled trigger that will run weekly, just to be safe :).

Next, we will need two HTTP Request actions. The first action will generate a new access token for our external trigger workflow.

  • Leave “Authentication Type” empty
  • Set the “Request URL” to the following, replacing {tenant} with your tenant: https://{tenant}.api.identitynow.com/oauth/token
  • Add three query parameters
    • grant_type - client_credentials
    • client_id - {your client id}
    • client_secret - {your client secret}
  • Set the “Method” to “POST”
  • Leave the “Request Content Type” empty

CAUTION: Query parameters aren’t encrypted when downloading workflow scripts. Since we have to supply a client ID and client secret in the query params, exercise caution when downloading and sharing the workflow script


Update the trigger subscription with the access token

Now that we have the access token, we need to update the trigger subscription with this new access token. Add another “HTTP Request” action to your workflow.

  • For the “Authentication Type”, set it to “OAuth 2.0”. We need to authenticate with our PAT to call the trigger subscription update API.
  • Set the “Token URL” to https://{tenant}.api.identitynow.com/oauth/token . Be sure to replace {tenant} with your tenant.
  • Set the Client ID and Client Secret to the values provided by your personal access token. See this document for more information on creating a personal access token. Also, make sure you have the sp:scopes:all scope.
  • Set the “Credentials Location” to “Header”
  • Set the “Request URL” to https://{tenant}.api.identitynow.com/beta/trigger-subscriptions/{subscriptionId}
    • You can get the subscriptionId from the list subscriptions endpoint. You will need to find the id of the subscription that has the same name as configured in the subscriptions UI.
  • Set the “Method” to “PATCH”.
  • Set the “Request Content Type” to “JSON Patch”
  • Set the “Request Body” as shown below. This request body uses an inline variable to insert the new access_token in the bearerToken attribute.
[
    {
        "op": "replace",
        "path": "/httpConfig/bearerTokenAuthConfig/bearerToken",
        "value": "{{$.hTTPRequest.body.access_token}}"
    }
]



Test the workflow

Finish with an “End Step - Success” operator and test the workflow.

image

Final steps

Now that we have the scaffolding created, you can revisit the first workflow and add additional logic to approve or deny requests. A caveat of request response type triggers is that you can only have a single subscription at a time, while fire and forget triggers can have up to 50. Because of this limitation, you probably won’t want to apply a filter to the event trigger subscription, and instead do some if/else logic in your workflow to handle the different types of requests. Once your logic is in place, enable all the trigger subscription and both workflows to start managing your access requests!

19 Likes

Hi @colin_mckibben , thank you for the detailed explanation. Just wanna check with the current configuration, will it be able to detect SoD violations if any?

We configured the current workflow as you mentioned, however when submitting access for a conflicting access within a SoD policy, we don’t see a violation detected.

Any thoughts on this?

I believe the SOD check happens before the event is sent. If the SOD passes, then the event is sent and the access request awaits a decision from the subscriber. If the SOD fails, then the access request should fail and not send the event. At least, this is how I interpret it.

For example, here is an access request where the SOD check happens first and passes, and then the event is sent to the subscriber as indicated by the “Access Request Preliminary Processing”.

image

I expect that if the SOD check failed, then it would never get to the “Access Request Preliminary Processing” stage.

Are you observing differently?

Hi @colin_mckibben , as per our observation so far, we see the same behavior as you mentioned in the two scenarios we tested

Consider, we have configured an SoD policy with two conflicting roles A & B

Scenario1: Submitted an access request for a user for role A. User doesn’t have any conflicting role.

Scenario2: Now, user has role A and submitted an access request for a conflicting access which is role B.
In scenario2, our expectation is that there should be SoD violation detected.

However, for the above 2 scenarios, we see the same behavior.
image

This sounds like a bug. I would expect that SOD step to fail and not continue on. Can you please submit a support ticket with the information you shared here?

Hey @colin_mckibben , would like to get some clarity regarding the expectation of this workflow when there should be an SoD violation detected.

So, as per your previous statement, if there is a SoD violation that should be detected, then the access request should fail at the SoD step.

Please confirm if my understanding is correct, so that I can raise a support ticket accordingly. Thank you!

Sure. Here is what I would include in the support ticket:

Observation

When implementing the Access Request Preapproval trigger, I noticed that access requests that should have failed due to an SOD policy no longer fail, and they continue to be processed by the preapproval trigger and ultimately approved.

Expected outcome

The SOD policy should be evaluated first for any access requests. If the SOD policy fails, the access request should fail. If the SOD policy succeeds, then the preapproval trigger should send the event to the subscriber for further processing.

Actual outcome

An access request is submitted that would fail the SOD policy if the event trigger wasn’t enabled, but since the trigger is enabled it passes the SOD policy and continues to fire the event.

How to reproduce

Please include your steps to reproduce, including screenshots of the SOD policy, the access item that should fail, and the results of the access request in request center.

2 Likes

Hi @colin_mckibben , we did few more rounds of testings from our end.

We could see, when we are submitting access request for a conflicting access, the SoD violation is detected and the access request is failed

Please find below our expectation by configuring this workflow and the subscribing to the event trigger:

When there is a SoD violation detected, by deafult, we want the approval to go to the Violation Owners configured in the SoD policy. Is this something that we can achieve using this trigger and the workflow? Please let us know. Thank you!

@Sravya2931

Just to clarify, did the SOD violation in your image happen when the workflow and trigger were disabled? If you enable the workflow and trigger and perform the exact same request, does it pass the SOD check and complete the request?

Also, did you submit a support ticket? If so, can you please share the case number?

Hey @colin_mckibben , the screenshot I attached earlier is when both workflow and trigger subscription are enabled.

If I disable the workflow and the trigger subscription, I could see the normal access request flow

I haven’t raised a support request yet as I am doing few more rounds of testings from our end.

Can you please confirm if we can achieve our expectation with this workflow?

When there is a SoD violation detected, by deafult, we want the approval to go to the Violation Owners configured in the SoD policy

Can you please confirm on my above query?

Thank you!

I don’t have SOD enabled in my test tenant so I am unable to verify your query. I suggest opening the support ticket with the information you have collected so they can attempt to reproduce it.

Hello @colin_mckibben and thanks a ton for this interesting article.
I have 2 questions :

  1. In the second Workflow in charge of updating the trigger’s Bearer token, in the second HTTP call in charge of patching the subscription, you mention :

Set the “Credentials Location” to “Header”

Shouldn’t it be set to “Body” instead ?

  1. Can you confirm that this PATCH call results in a 200 response but that the trigger subscription still shows the bearerTokenAuthConfig/bearerToken as null like so, even after the successful call :
{
  "httpConfig": {
    "httpDispatchMode": "ASYNC",
    "httpAuthenticationType": "BEARER_TOKEN",
    "basicAuthConfig": null,
    "bearerTokenAuthConfig": {
      "bearerToken": null
    },
    "url": "https://xxxxxxxxxxxxxxxxxxxx/beta/workflows/execute/external/yyyyyyyyyyyyyyyyyyyyy"
  },
  "responseDeadline": "PT1H",
  "triggerName": "Access Request Submitted",
  "triggerId": "idn:access-request-pre-approval",
  "description": "",
  "enabled": true,
  "id": "823a7f0b-14c0-4e3f-97a8-f8fbfe47e1f7",
  "type": "HTTP",
  "name": "My subscription"
}

Thanks in advance
Christophe

1 Like

Another note about this warning :

It is actually possible to benefit from the client and server side encryption of those credentials as well as their protection against exports by using the Basic auth scheme for this call with a POST method and a query parameter grant_type set to client_credentials.

Thanks.

2 Likes

I haven’t tried to use “Body” when setting credential location. “Header” worked in this case.

And yes, the PATCH call should return null for the bearerToken so the token isn’t displayed in plaintext, which can be a security concern as these responses are often logged.

1 Like

Hey Colin, quick note : in your first curl command, to get the first access token, I think you need to pass the HTTP method (POST) with the -X parameter. Something like curl -X POST ....
For the rest, I like so much your creativity for putting in place this workaround.

Thanks Olivier. I updated the blog accordingly.

@colin_mckibben - I have followed the above steps and the workflow is getting triggered without any issue but access request is not completing the Approval, Still it is going through the approval process.

{
	"name": "Bulk Access request approval bypass",
	"description": "Bulk Access request approval bypass",
	"modified": "2024-02-15T06:09:24.89935385Z",
	"modifiedBy": {
		"type": "IDENTITY",
		"id": "495aa92bxxxxxxxxxxxxxxxxx",
		"name": "xxxxxxxx"
	},
	"definition": {
		"start": "HTTP Request",
		"steps": {
			"End Step - Success": {
				"description": "Success",
				"displayName": "",
				"type": "success"
			},
			"HTTP Request": {
				"actionId": "sp:http",
				"attributes": {
					"jsonRequestBody": {
						"output": {
							"approved": true,
							"approver": "Workflow",
							"comment": "This access has passed workflow approval."
						},
						"secret": "{{$.trigger._metadata.secret}}"
					},
					"method": "post",
					"requestContentType": "json",
					"url.$": "$.trigger._metadata.callbackURL"
				},
				"displayName": "",
				"nextStep": "End Step - Success",
				"type": "action",
				"versionNumber": 2
			}
		}
	},
	"creator": {
		"type": "IDENTITY",
		"id": "495aa92b91xxxxxxxxxxxxxxxxxx",
		"name": "hgxxxxxxx"
	},
	"trigger": {
		"type": "EXTERNAL",
		"attributes": {
			"clientId": "f6e20dxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9",
			"url": "/beta/workflows/execute/external/521540xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
		}
	}
}

This is the expected behavior for Access Request Submitted | SailPoint Developer Community. It does not bypass the approval chain, it only adds an approval step at the beginning of the approval chain.

1 Like