mirror of
https://github.com/LukeHagar/developer.sailpoint.com.git
synced 2025-12-06 12:27:46 +00:00
updated event trigger docs formatting
This commit is contained in:
@@ -69,7 +69,7 @@ const config = {
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
additionalLanguages: ["ruby", "csharp", "php"],
|
||||
additionalLanguages: ["http", "java"],
|
||||
},
|
||||
}),
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ sidebar_position: 4
|
||||
|
||||
## What is a filter
|
||||
|
||||
Many triggers can produce a staggering amount of events if left unfiltered, resulting in more network traffic and more processing time on a subscribing service. Most of the time, your subscribing service only needs to be notified of events that contain a key attribute or value that you are interested in processing. For example, the Identity Attributes Changed trigger will emit an event every time an identity has a change in attributes. This can occur during the mover process when an identity changes departments, or a manager is promoted resulting in several identities receiving a new manager. Rather than inundate your subscribing service with every identity change, you can use an event trigger filter to specify which events your service is interested in processing.
|
||||
Many triggers can produce a staggering amount of events if left unfiltered, resulting in more network traffic and more processing time on a subscribing service. Most of the time, your subscribing service only needs to be notified of events that contain a key attribute or value that you are interested in processing. For example, the Identity Attributes Changed trigger will emit an event every time an identity has a change in attributes. This can occur during the mover process when an identity changes departments, or a manager is promoted resulting in several identities receiving a new manager. Rather than inundate your subscribing service with every identity change, you can use an event trigger filter to specify which events your service is interested in processing.
|
||||
|
||||
## Benefits of using filters
|
||||
|
||||
Network bandwidth and processing power come at a cost, especially when using managed solutions like AWS or no code providers like Zapier. Without filtering, a subscribing service would be sent every single event that the trigger receives. The first thing that any subscriber would need to do in this scenario is inspect each event to figure out which ones it needs to process and which ones it can ignore. Taking this approach with managed providers that charge per invocation, like AWS Lambda, can become expensive. Furthermore, some no-code providers might put a limit on the total number of invocations that a service can make in a given month, which would be quickly exhausted with this approach. Trigger filters take the filtering logic out of your subscribing service and place it on the event trigger within SailPoint, so you only receive the events that match your filter criteria.
|
||||
Network bandwidth and processing power come at a cost, especially when using managed solutions like AWS or no code providers like Zapier. Without filtering, a subscribing service would be sent every single event that the trigger receives. The first thing that any subscriber would need to do in this scenario is inspect each event to figure out which ones it needs to process and which ones it can ignore. Taking this approach with managed providers that charge per invocation, like AWS Lambda, can become expensive. Furthermore, some no-code providers might put a limit on the total number of invocations that a service can make in a given month, which would be quickly exhausted with this approach. Trigger filters take the filtering logic out of your subscribing service and place it on the event trigger within SailPoint, so you only receive the events that match your filter criteria.
|
||||
|
||||
## Constructing a filter
|
||||
|
||||
@@ -19,7 +19,7 @@ Filters are constructed using a [Goessner JSONpath expression](https://goessner.
|
||||
|
||||
### Expressions
|
||||
|
||||
JSONPath expressions specify a path to an element, or array of elements, in a JSON structure. Expressions are used to select data in a JSON structure to check for the existence of attributes or to narrow done the data where the filter logic will be applied.
|
||||
JSONPath expressions specify a path to an element, or array of elements, in a JSON structure. Expressions are used to select data in a JSON structure to check for the existence of attributes or to narrow done the data where the filter logic will be applied.
|
||||
|
||||
| Expression | Description | Example |
|
||||
| --- | --- | --- |
|
||||
@@ -42,9 +42,9 @@ JSONPath operators provide more options to filter JSON structures.
|
||||
|
||||
| Operator | Description | Example |
|
||||
| --- | --- | --- |
|
||||
| == | **Equals to** - Evaluates to `true` if operands match. | $[?($.identity.name == "john.doe")] |
|
||||
| == | **Equals to** - Evaluates to `true` if operands match. | $[?($.identity.name == "john.doe")] |
|
||||
| != | **Not equal to** - Evaluates to `true` if operands don't match. | $[?($.identity.name != "george.washington")] |
|
||||
| > | **Greater than** - Evaluates to `true` if the left operand is greater than the right operand. Works on strings and numbers. | $[?($.attributes.created > '2020-04-27T16:48:33.200Z')] |
|
||||
| > | **Greater than** - Evaluates to `true` if the left operand is greater than the right operand. Works on strings and numbers. | $[?($.attributes.created > '2020-04-27T16:48:33.200Z')] |
|
||||
| >= | **Greater than or equal to** - Evaluates to `true` if the left operand is greater than or equal to the right operand. | $[?($.attributes.created >= '2020-04-27T16:48:33.597Z')] |
|
||||
| < | **Less than** - Evaluates to `true` if the left operand is less than the right operand. | $[?($.attributes.created < '2020-04-27T16:48:33.200Z')] |
|
||||
| <= | **Less than or equal to** - Evaluates to `true` if the left operand is less than or equal to the right operand. | $[?($.attributes.created <= '2020-04-27T16:48:33.200Z')] |
|
||||
@@ -55,109 +55,111 @@ JSONPath operators provide more options to filter JSON structures.
|
||||
|
||||
### Developing Filters
|
||||
|
||||
Development of a filter can go faster when using a tool like an online [JSONpath editor](https://jsonpath.herokuapp.com/). These tools can provide quick feedback on your filter, allowing you to hone in on the exact filter expression you want before testing it in IdentityNow. Be aware, however, that these online tools might have subtle differences compared with SailPoint's implementation of Goessner JSONpath. Always test your JSONpath filter in IdentityNow before using it in production.
|
||||
Development of a filter can go faster when using a tool like an online [JSONpath editor](https://jsonpath.herokuapp.com/). These tools can provide quick feedback on your filter, allowing you to hone in on the exact filter expression you want before testing it in IdentityNow. Be aware, however, that these online tools might have subtle differences compared with SailPoint's implementation of Goessner JSONpath. Always test your JSONpath filter in IdentityNow before using it in production.
|
||||
|
||||
Start by opening a [JSONpath editor](https://jsonpath.herokuapp.com/) in your browser. Make sure that the correct implementation is selected, if there is more than one option. You can then paste in an example trigger input and start crafting your JSONpath expression.
|
||||
Start by opening a [JSONpath editor](https://jsonpath.herokuapp.com/) in your browser. Make sure that the correct implementation is selected, if there is more than one option. You can then paste in an example trigger input and start crafting your JSONpath expression.
|
||||
|
||||

|
||||
|
||||
Most of the examples provided in the operator tables above can be used against the Identity Attributes Changed event trigger input, as seen below. You can find all of the input/output schemas for the other available triggers in our [API specification](/idn/api/beta/triggers#available-event-triggers).
|
||||
Most of the examples provided in the operator tables above can be used against the Identity Attributes Changed event trigger input, as seen below. You can find all of the input/output schemas for the other available triggers in our [API specification](/idn/api/beta/triggers#available-event-triggers).
|
||||
|
||||
```json
|
||||
{
|
||||
"identity": {
|
||||
"id": "ee769173319b41d19ccec6cea52f237b",
|
||||
"name": "john.doe",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"attribute": "department",
|
||||
"oldValue": "Sales",
|
||||
"newValue": "Marketing"
|
||||
},
|
||||
{
|
||||
"attribute": "manager",
|
||||
"oldValue": {
|
||||
"id": "ee769173319b41d19ccec6c235423237b",
|
||||
"name": "robert.brown",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"newValue": {
|
||||
"id": "ee769173319b41d19ccec6c235423236c",
|
||||
"name": "mary.johnson",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
},
|
||||
{
|
||||
"attribute": "cloudLifecycleState",
|
||||
"oldValue": "active",
|
||||
"newValue": "terminated"
|
||||
}
|
||||
]
|
||||
"identity": {
|
||||
"id": "ee769173319b41d19ccec6cea52f237b",
|
||||
"name": "john.doe",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"attribute": "department",
|
||||
"oldValue": "Sales",
|
||||
"newValue": "Marketing"
|
||||
},
|
||||
{
|
||||
"attribute": "manager",
|
||||
"oldValue": {
|
||||
"id": "ee769173319b41d19ccec6c235423237b",
|
||||
"name": "robert.brown",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"newValue": {
|
||||
"id": "ee769173319b41d19ccec6c235423236c",
|
||||
"name": "mary.johnson",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
},
|
||||
{
|
||||
"attribute": "cloudLifecycleState",
|
||||
"oldValue": "active",
|
||||
"newValue": "terminated"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Validating filters
|
||||
|
||||
When you are finished developing your JSONpath filter, you need to validate it with SailPoint's trigger service. There are two ways to do this: with the UI or the API.
|
||||
When you are finished developing your JSONpath filter, you need to validate it with SailPoint's trigger service. There are two ways to do this: with the UI or the API.
|
||||
|
||||
### Validating filters using the UI
|
||||
|
||||
To validate a filter using the UI, subscribe to a new event trigger or edit an existing one. In the configuration options, paste your JSONpath expression in the `Filter` input box and then click `Update`. If you don't receive an error message, then your filter expression should be valid with SailPoint.
|
||||
To validate a filter using the UI, subscribe to a new event trigger or edit an existing one. In the configuration options, paste your JSONpath expression in the `Filter` input box and then click `Update`. If you don't receive an error message, then your filter expression should be valid with SailPoint.
|
||||
|
||||

|
||||
|
||||
### Validating filters using the API
|
||||
|
||||
You can validate a trigger filter using the [validate filter](/idn/api/beta/validate-filter) API endpoint. You will need to escape any double quotes, as seen in the example payload in the API description. Also, you will need to provide a sample input for the validation engine to run against. It is best to use the input example included in the input/output schemas for the event trigger you wish to apply your filter to. Please see [this table](/idn/api/beta/triggers#available-event-triggers) to find the schema of your event trigger. An example request is as follows:
|
||||
You can validate a trigger filter using the [validate filter](/idn/api/beta/validate-filter) API endpoint. You will need to escape any double quotes, as seen in the example payload in the API description. Also, you will need to provide a sample input for the validation engine to run against. It is best to use the input example included in the input/output schemas for the event trigger you wish to apply your filter to. Please see [this table](/idn/api/beta/triggers#available-event-triggers) to find the schema of your event trigger. An example request is as follows:
|
||||
|
||||
POST `https://{tenant}.api.identitynow.com/beta/trigger-subscriptions/validate-filter`
|
||||
```text
|
||||
POST https://{tenant}.api.identitynow.com/beta/trigger-subscriptions/validate-filter
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"input": {
|
||||
"identity": {
|
||||
"id": "ee769173319b41d19ccec6cea52f237b",
|
||||
"name": "john.doe",
|
||||
"type": "IDENTITY"
|
||||
"input": {
|
||||
"identity": {
|
||||
"id": "ee769173319b41d19ccec6cea52f237b",
|
||||
"name": "john.doe",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"attribute": "department",
|
||||
"oldValue": "Sales",
|
||||
"newValue": "Marketing"
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"attribute": "department",
|
||||
"oldValue": "Sales",
|
||||
"newValue": "Marketing"
|
||||
},
|
||||
{
|
||||
"attribute": "manager",
|
||||
"oldValue": {
|
||||
"id": "ee769173319b41d19ccec6c235423237b",
|
||||
"name": "robert.brown",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"newValue": {
|
||||
"id": "ee769173319b41d19ccec6c235423236c",
|
||||
"name": "mary.johnson",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
},
|
||||
{
|
||||
"attribute": "cloudLifecycleState",
|
||||
"oldValue": "active",
|
||||
"newValue": "terminated"
|
||||
}
|
||||
]
|
||||
{
|
||||
"attribute": "manager",
|
||||
"oldValue": {
|
||||
"id": "ee769173319b41d19ccec6c235423237b",
|
||||
"name": "robert.brown",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"newValue": {
|
||||
"id": "ee769173319b41d19ccec6c235423236c",
|
||||
"name": "mary.johnson",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
},
|
||||
{
|
||||
"attribute": "cloudLifecycleState",
|
||||
"oldValue": "active",
|
||||
"newValue": "terminated"
|
||||
}
|
||||
]
|
||||
},
|
||||
"filter": "$[?($.identity.name == \"john.doe\")]"
|
||||
"filter": "$[?($.identity.name == \"john.doe\")]"
|
||||
}
|
||||
```
|
||||
|
||||
## Testing filters
|
||||
|
||||
If your trigger filter is accepted by SailPoint, then you will want to test that it actually works. You will need to configure your trigger subscription to point to the URL of your testing service. [webhook.site](https://webhook.site) is an easy to use testing service. Just copy the unique URL that it generates and paste it into integration URL field of your subscription. The easist way to test a trigger subscription is to use the UI to fire off a test event.
|
||||
If your trigger filter is accepted by SailPoint, then you will want to test that it actually works. You will need to configure your trigger subscription to point to the URL of your testing service. One easy service, mention other times in our documentation, is [webhook.site](https://webhook.site). Just copy the unique URL that it generates and paste it into integration URL field of your subscription. The easist way to test a trigger subscription is to use the UI to fire off a test event.
|
||||
|
||||

|
||||
|
||||
Once you fire off a test event, monitor your webhook.site webpage for an incoming event. If the filter matches the test input, you should see an event come in. If the filter doesn't match the input, then it won't fire. It's good to test both scenarios to make sure your filter isn't always evaluating to `true`, and that it will indeed evaluate to `false` under the correct circumstances. For example, the filter `$[?($.identity.name contains "john")]` will match the test event for Identity Attributes Changed and you will see an event in webhook.site, but you will also want to make sure that `$[?($.identity.name contains "archer")]` doesn't fire, since the test input is always the same.
|
||||
Once you fire off a test event, monitor your webhook.site webpage for an incoming event. If the filter matches the test input, you should see an event come in. If the filter doesn't match the input, then it won't fire. It's good to test both scenarios to make sure your filter isn't always evaluating to `true`, and that it will indeed evaluate to `false` under the correct circumstances. For example, the filter `$[?($.identity.name contains "john")]` will match the test event for Identity Attributes Changed and you will see an event in [webhook.site](https://webhook.site), but you will also want to make sure that `$[?($.identity.name contains "archer")]` doesn't fire, since the test input is always the same.
|
||||
|
||||
If you want to control the test input to validate your filter against a more robust set of data, then you can use the [test invocation](/idn/api/beta/start-test-invocation) API endpoint.
|
||||
|
||||
@@ -5,28 +5,28 @@ sidebar_position: 2
|
||||
|
||||
# Preparing a Subscriber Service
|
||||
|
||||
Before you can subscribe to an event trigger, you need to prepare a service that can accept incoming HTTP requests from the event trigger service. More specifically, your client service needs to accept a POST request to an endpoint of its choosing, with the ability to parse the JSON data sent by the trigger. There are many ways to accomplish this, but we will cover four of the most common types of client services you can build to handle event triggers.
|
||||
Before you can subscribe to an event trigger, you need to prepare a service that can accept incoming HTTP requests from the event trigger service. More specifically, your client service needs to accept a POST request to an endpoint of its choosing, with the ability to parse the JSON data sent by the trigger. There are many ways to accomplish this, but we will cover four of the most common types of client services you can build to handle event triggers.
|
||||
|
||||
## Webhook testing service
|
||||
|
||||
There are numerous webhook testing websites that will generate a unique URL that you can use to subscribe to an event trigger and explore the data that is sent by the trigger. One such site is https://webhook.site. This site will generate a unique URL each time you open it, which you can copy and paste into the subscription configuration in IdentityNow. Any events that the trigger generates will be sent to this website for you to analyze.
|
||||
There are numerous webhook testing websites that will generate a unique URL that you can use to subscribe to an event trigger and explore the data that is sent by the trigger. One such site is [webhook.site](https://webhook.site). This site will generate a unique URL each time you open it, which you can copy and paste into the subscription configuration in IdentityNow. Any events that the trigger generates will be sent to this website for you to analyze.
|
||||
|
||||

|
||||
|
||||
The purpose of webhook testing services is to make it easy to set up a trigger and see the data of the events that will eventually be sent to your production service. This can aid in the early development process as you explore the data the event trigger sends and how best to access the data that you need.
|
||||
The purpose of webhook testing services is to make it easy to set up a trigger and see the data of the events that will eventually be sent to your production service. This can aid in the early development process as you explore the data the event trigger sends and how best to access the data that you need.
|
||||
|
||||
## Native SaaS workflows
|
||||
|
||||
Some SaaS vendors provide built-in workflow builders in their product so you don't have to use a no code provider. Slack, for example, has a premium [workflow builder](https://slack.com/help/articles/360035692513-Guide-to-Workflow-Builder) feature that will generate a unique URL that you can use to configure your subscription. Slack's workflow builder can then listen for events sent by your trigger and perform Slack specific actions on the data, like sending a user a message when their access request has been approved.
|
||||
Some SaaS vendors provide built-in workflow builders in their product so you don't have to use a no code provider. Slack, for example, has a premium [workflow builder](https://slack.com/help/articles/360035692513-Guide-to-Workflow-Builder) feature that will generate a unique URL that you can use to configure your subscription. Slack's workflow builder can then listen for events sent by your trigger and perform Slack specific actions on the data, like sending a user a message when their access request has been approved.
|
||||
|
||||

|
||||
|
||||
## No code provider
|
||||
|
||||
No code/low code providers, like Zapier and Microsoft Power Automate, make it very easy to consume event triggers and perform actions based on the event data. They are a popular solution for those looking to prototype or quickly create automated business processes, and cater to novices and advanced users alike. Each no code provider has documentation on how to create a new workflow and subscribe to an event trigger or webhook, so you will need to find the relevant documentation for your no code provider to learn how to set one up. Zapier has the ability to configure a webhook action that will generate a unique URL that you can configure in your event trigger subscription.
|
||||
No code/low code providers, like Zapier and Microsoft Power Automate, make it very easy to consume event triggers and perform actions based on the event data. They are a popular solution for those looking to prototype or quickly create automated business processes, and cater to novices and advanced users alike. Each no code provider has documentation on how to create a new workflow and subscribe to an event trigger or webhook, so you will need to find the relevant documentation for your no code provider to learn how to set one up. Zapier has the ability to configure a webhook action that will generate a unique URL that you can configure in your event trigger subscription.
|
||||
|
||||

|
||||
|
||||
## Custom application
|
||||
|
||||
A custom application is one that you write in a language of your choosing and host in your own infrastructure, cloud or on-premise. This is the most advanced option for implementing an event trigger client service. Although it requires a great deal of skill and knowledge to build, deploy, and operate your own service that can consume requests over HTTP, it offers the most power and flexibility to implement your use cases. You can learn more about custom applications by checking out our [Event Trigger Example Application](https://github.com/sailpoint-oss/event-trigger-examples).
|
||||
A custom application is one that you write in a language of your choosing and host in your own infrastructure, cloud or on-premise. This is the most advanced option for implementing an event trigger client service. Although it requires a great deal of skill and knowledge to build, deploy, and operate your own service that can consume requests over HTTP, it offers the most power and flexibility to implement your use cases. You can learn more about custom applications by checking out our [Event Trigger Example Application](https://github.com/sailpoint-oss/event-trigger-examples).
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
---
|
||||
id: responding-to-request-response-trigger
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Responding to a Request Response Trigger
|
||||
|
||||
## Invocation response modes for REQUEST_RESPONSE type triggers
|
||||
|
||||
You can specify how your application interacts with a `REQUEST_RESPONSE` type trigger service by selecting an invocation response mode in the **Response Type** dropdown when editing or creating a `REQUEST_RESPONSE` subscription. There are three response modes to choose from: `SYNC`, `ASYNC`, and `DYNAMIC`. These response modes are only available when the subscription type is set to `HTTP`.
|
||||
|
||||
- `SYNC`: This type of response creates a *synchronous* flow between the trigger service and the custom application. Once a trigger has been invoked, the custom application is expected to respond within 10 seconds. If the application takes longer than 10 seconds to respond, the trigger invocation will terminate without making any decisions.
|
||||
- `ASYNC`: This type of response creates an *asynchronous* flow between the trigger service and the custom application. When a trigger is invoked, the custom application does not need to respond immediately. The trigger service will provide a URL and a secret that the custom application can use to complete the invocation at a later time. The application must complete the invocation before the configured deadline on the subscription.
|
||||
- `DYNAMIC`: This type of response gives the custom application the ability to choose whether it handles the invocation request synchronously or asynchronously on a per-event basis. In some cases, the application may choose `SYNC` mode because it is able to respond quickly to the invocation. In other cases, it may choose `ASYNC` because it needs to run a long running task before responding to the invocation.
|
||||
|
||||
## Responding to a REQUEST_RESPONSE trigger invocation
|
||||
|
||||
### SYNC response
|
||||
|
||||
<!-- Uncomment this once the model definition links are fixed
|
||||
The custom application responds to the trigger invocation with an appropriate payload. For example, the application may receive a request from the [Access Request Dynamic Approver](https://developer.sailpoint.com/apis/beta/#tag/Event-Trigger-Models) trigger. The application will have **10 seconds** to analyze the event details and respond with a 200 (OK) status code and a [response payload](https://developer.sailpoint.com/apis/beta/#section/Access-Request-Dynamic-Approver-Event-Trigger-Output) that contains the identity to add to the approval chain. -->
|
||||
|
||||
The custom application responds to the trigger invocation with an appropriate payload. For example, the application may receive a request from the Access Request Dynamic Approver trigger. The application will have **10 seconds** to analyze the event details and respond with a 200 (OK) status code and a response payload that contains the identity to add to the approval chain. For example, the response may look like this:
|
||||
|
||||
200 (OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Adams",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
```
|
||||
|
||||
### ASYNC response
|
||||
|
||||
<!-- Uncomment this once the model definition links are fixed
|
||||
The custom application only needs to acknowledge that it has received the trigger invocation request by returning an HTTP status of 200 (OK) with an empty JSON object (ex. `{}`) in the response body within **10 seconds** of receiving the event. It then has until the configured deadline on the subscription to provide a full response to the invocation. For example, the application may receive a request from the [Access Request Dynamic Approver](https://developer.sailpoint.com/apis/beta/#tag/Event-Trigger-Models) trigger. An example of the request payload that the application might receive is as follows: -->
|
||||
|
||||
The custom application only needs to acknowledge that it has received the trigger invocation request by returning an HTTP status of 200 (OK) with an empty JSON object (ex. `{}`) in the response body within **10 seconds** of receiving the event. It then has until the configured deadline on the subscription to provide a full response to the invocation. For example, the application may receive a request from the Access Request Dynamic Approver trigger. An example of the request payload that the application might receive is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"_metadata": {
|
||||
"callbackURL": "https://{tenant}.api.identitynow.com/beta/trigger-invocations/e9103ca9-02c4-bb0f-9441-94b3af012345/complete",
|
||||
"responseMode": "async",
|
||||
"secret": "c1c60493-3347-4550-9c00-123cdde",
|
||||
"triggerId": "idn:access-request-dynamic-approver",
|
||||
"triggerType": "requestResponse"
|
||||
},
|
||||
"accessRequestId": "4b4d982dddff4267ab12f0f1e72b5a6d",
|
||||
"requestedBy": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Admin",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"requestedFor": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470909",
|
||||
"name": "Ed Engineer",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"requestedItems": [
|
||||
{
|
||||
"comment": "Ed needs this access for his day to day job activities",
|
||||
"description": "Engineering Access",
|
||||
"id": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"name": "Engineering Access",
|
||||
"operation": "Add",
|
||||
"type": "ACCESS_PROFILE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The application will immediately respond to the invocation with a 200 (OK) status code and an empty JSON object.
|
||||
|
||||
200 (OK)
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
Once the application has made a decision on how to respond, it will use the `callbackURL` and `secret` provided in the `_metadata` object from the original request to complete the invocation. An example response might look like the following:
|
||||
|
||||
POST `https://{tenant}.api.identitynow.com/beta/trigger-invocations/e9103ca9-02c4-bb0f-9441-94b3af012345/complete`
|
||||
|
||||
```json
|
||||
{
|
||||
"secret": "0f11f2a4-7c94-4bf3-a2bd-742580fe3bde",
|
||||
"output": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Adams",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### DYNAMIC response
|
||||
|
||||
The custom application determines arbitrarily whether to respond to the trigger invocation as `SYNC` or `ASYNC`. If the application wishes to respond as `SYNC`, it should follow the directions for a `SYNC` response type, responding within **10 seconds** of the invocation. In the case of `ASYNC`, the custom application only needs to acknowledge that it has received the trigger invocation request with a 202 (Accepted) within **10 seconds** of receiving the event and complete the invocation at a later time using the `callbackURL` and `secret` provided in the `_metadata` object.
|
||||
|
||||
An example of the request payload that the application might receive is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"_metadata": {
|
||||
"callbackURL": "https://{tenant}.api.identitynow.com/beta/trigger-invocations/e9103ca9-02c4-bb0f-9441-94b3af012345/complete",
|
||||
"responseMode": "async",
|
||||
"secret": "c1c60493-3347-4550-9c00-123cdde",
|
||||
"triggerId": "idn:access-request-dynamic-approver",
|
||||
"triggerType": "requestResponse"
|
||||
},
|
||||
"accessRequestId": "4b4d982dddff4267ab12f0f1e72b5a6d",
|
||||
"requestedBy": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Admin",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"requestedFor": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470909",
|
||||
"name": "Ed Engineer",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"requestedItems": [
|
||||
{
|
||||
"comment": "Ed needs this access for his day to day job activities",
|
||||
"description": "Engineering Access",
|
||||
"id": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"name": "Engineering Access",
|
||||
"operation": "Add",
|
||||
"type": "ACCESS_PROFILE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
To respond as `SYNC`, simply respond to the invocation within 10 seconds.
|
||||
|
||||
200 (OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Adams",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
```
|
||||
|
||||
To respond as `ASYNC`, start by responding to the invocation with a 202 (Accepted).
|
||||
|
||||
202 (Accepted)
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
Then, use the `callbackURL` and `secret` to send a POST request to the invocation with the decision.
|
||||
|
||||
POST `https://{tenant}.api.identitynow.com/beta/trigger-invocations/e9103ca9-02c4-bb0f-9441-94b3af012345/complete`
|
||||
|
||||
```json
|
||||
{
|
||||
"secret": "0f11f2a4-7c94-4bf3-a2bd-742580fe3bde",
|
||||
"output": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Adams",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Trigger invocation status
|
||||
|
||||
To check the status of a particular trigger invocation, you can use the [list invocation statuses](/idn/api/beta/list-invocation-status) endpoint. The status endpoint works for both `REQUEST_RESPONSE` and `FIRE_AND_FORGET` triggers. However, the status of `FIRE_AND_FORGET` trigger invocations will contain null values in their `completeInvocationInput` since `FIRE_AND_FORGET` triggers don't need a response to complete.
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
id: responding-to-request-response-trigger
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Responding to a Request Response Trigger
|
||||
|
||||
## Invocation response modes for REQUEST_RESPONSE type triggers
|
||||
|
||||
You can specify how your application interacts with a `REQUEST_RESPONSE` type trigger service by selecting an invocation response mode in the **Response Type** dropdown when editing or creating a `REQUEST_RESPONSE` subscription. There are three response modes to choose from: `SYNC`, `ASYNC`, and `DYNAMIC`. These response modes are only available when the subscription type is set to `HTTP`.
|
||||
|
||||
| Response Modes | Description |
|
||||
|----------|:-------------:|
|
||||
| `SYNC` | This type of response creates a *synchronous* flow between the trigger service and the custom application. Once a trigger has been invoked, the custom application is expected to respond within 10 seconds. If the application takes longer than 10 seconds to respond, the trigger invocation will terminate without making any decisions. |
|
||||
| `ASYNC` | This type of response creates an *asynchronous* flow between the trigger service and the custom application. When a trigger is invoked, the custom application does not need to respond immediately. The trigger service will provide a URL and a secret that the custom application can use to complete the invocation at a later time. The application must complete the invocation before the configured deadline on the subscription.|
|
||||
| `DYNAMIC` | This type of response gives the custom application the ability to choose whether it handles the invocation request synchronously or asynchronously on a per-event basis. In some cases, the application may choose `SYNC` mode because it is able to respond quickly to the invocation. In other cases, it may choose `ASYNC` because it needs to run a long running task before responding to the invocation.|
|
||||
|
||||
## Responding to REQUEST_RESPONSE trigger
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sync" label="SYNC Response" default>
|
||||
|
||||
<!-- Uncomment this once the model definition links are fixed
|
||||
The custom application responds to the trigger invocation with an appropriate payload. For example, the application may receive a request from the [Access Request Dynamic Approver](https://developer.sailpoint.com/apis/beta/#tag/Event-Trigger-Models) trigger. The application will have **10 seconds** to analyze the event details and respond with a 200 (OK) status code and a [response payload](https://developer.sailpoint.com/apis/beta/#section/Access-Request-Dynamic-Approver-Event-Trigger-Output) that contains the identity to add to the approval chain. -->
|
||||
|
||||
The custom application responds to the trigger invocation with an appropriate payload. For example, the application may receive a request from the Access Request Dynamic Approver trigger. The application will have **10 seconds** to analyze the event details and respond with a 200 (OK) status code and a response payload that contains the identity to add to the approval chain. For example, the response may look like this:
|
||||
|
||||
200 (OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Adams",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="async" label="ASYNC Respose">
|
||||
|
||||
<!-- Uncomment this once the model definition links are fixed
|
||||
The custom application only needs to acknowledge that it has received the trigger invocation request by returning an HTTP status of 200 (OK) with an empty JSON object (ex. `{}`) in the response body within **10 seconds** of receiving the event. It then has until the configured deadline on the subscription to provide a full response to the invocation. For example, the application may receive a request from the [Access Request Dynamic Approver](https://developer.sailpoint.com/apis/beta/#tag/Event-Trigger-Models) trigger. An example of the request payload that the application might receive is as follows: -->
|
||||
|
||||
The custom application only needs to acknowledge that it has received the trigger invocation request by returning an HTTP status of 200 (OK) with an empty JSON object (ex. `{}`) in the response body within **10 seconds** of receiving the event. It then has until the configured deadline on the subscription to provide a full response to the invocation. For example, the application may receive a request from the Access Request Dynamic Approver trigger. An example of the request payload that the application might receive is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"_metadata": {
|
||||
"callbackURL": "https://{tenant}.api.identitynow.com/beta/trigger-invocations/e9103ca9-02c4-bb0f-9441-94b3af012345/complete",
|
||||
"responseMode": "async",
|
||||
"secret": "c1c60493-3347-4550-9c00-123cdde",
|
||||
"triggerId": "idn:access-request-dynamic-approver",
|
||||
"triggerType": "requestResponse"
|
||||
},
|
||||
"accessRequestId": "4b4d982dddff4267ab12f0f1e72b5a6d",
|
||||
"requestedBy": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Admin",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"requestedFor": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470909",
|
||||
"name": "Ed Engineer",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"requestedItems": [
|
||||
{
|
||||
"comment": "Ed needs this access for his day to day job activities",
|
||||
"description": "Engineering Access",
|
||||
"id": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"name": "Engineering Access",
|
||||
"operation": "Add",
|
||||
"type": "ACCESS_PROFILE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The application will immediately respond to the invocation with a 200 (OK) status code and an empty JSON object.
|
||||
|
||||
200 (OK)
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
Once the application has made a decision on how to respond, it will use the `callbackURL` and `secret` provided in the `_metadata` object from the original request to complete the invocation. An example response might look like the following:
|
||||
|
||||
POST `https://{tenant}.api.identitynow.com/beta/trigger-invocations/e9103ca9-02c4-bb0f-9441-94b3af012345/complete`
|
||||
|
||||
```json
|
||||
{
|
||||
"secret": "0f11f2a4-7c94-4bf3-a2bd-742580fe3bde",
|
||||
"output": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Adams",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="dynamic" label="DYNAMIC Response">
|
||||
|
||||
The custom application determines arbitrarily whether to respond to the trigger invocation as `SYNC` or `ASYNC`. If the application wishes to respond as `SYNC`, it should follow the directions for a `SYNC` response type, responding within **10 seconds** of the invocation. In the case of `ASYNC`, the custom application only needs to acknowledge that it has received the trigger invocation request with a 202 (Accepted) within **10 seconds** of receiving the event and complete the invocation at a later time using the `callbackURL` and `secret` provided in the `_metadata` object.
|
||||
|
||||
An example of the request payload that the application might receive is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"_metadata": {
|
||||
"callbackURL": "https://{tenant}.api.identitynow.com/beta/trigger-invocations/e9103ca9-02c4-bb0f-9441-94b3af012345/complete",
|
||||
"responseMode": "async",
|
||||
"secret": "c1c60493-3347-4550-9c00-123cdde",
|
||||
"triggerId": "idn:access-request-dynamic-approver",
|
||||
"triggerType": "requestResponse"
|
||||
},
|
||||
"accessRequestId": "4b4d982dddff4267ab12f0f1e72b5a6d",
|
||||
"requestedBy": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Admin",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"requestedFor": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470909",
|
||||
"name": "Ed Engineer",
|
||||
"type": "IDENTITY"
|
||||
},
|
||||
"requestedItems": [
|
||||
{
|
||||
"comment": "Ed needs this access for his day to day job activities",
|
||||
"description": "Engineering Access",
|
||||
"id": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"name": "Engineering Access",
|
||||
"operation": "Add",
|
||||
"type": "ACCESS_PROFILE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
To respond as `SYNC`, simply respond to the invocation within 10 seconds.
|
||||
|
||||
200 (OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Adams",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
```
|
||||
|
||||
To respond as `ASYNC`, start by responding to the invocation with a 202 (Accepted).
|
||||
|
||||
202 (Accepted)
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
Then, use the `callbackURL` and `secret` to send a POST request to the invocation with the decision.
|
||||
|
||||
POST `https://{tenant}.api.identitynow.com/beta/trigger-invocations/e9103ca9-02c4-bb0f-9441-94b3af012345/complete`
|
||||
|
||||
```json
|
||||
{
|
||||
"secret": "0f11f2a4-7c94-4bf3-a2bd-742580fe3bde",
|
||||
"output": {
|
||||
"id": "2c91808b6ef1d43e016efba0ce470906",
|
||||
"name": "Adam Adams",
|
||||
"type": "IDENTITY"
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Trigger invocation status
|
||||
|
||||
To check the status of a particular trigger invocation, you can use the [list invocation statuses](/idn/api/beta/list-invocation-status) endpoint. The status endpoint works for both `REQUEST_RESPONSE` and `FIRE_AND_FORGET` triggers. However, the status of `FIRE_AND_FORGET` trigger invocations will contain null values in their `completeInvocationInput` since `FIRE_AND_FORGET` triggers don't need a response to complete.
|
||||
@@ -7,7 +7,7 @@ sidebar_position: 3
|
||||
|
||||
## View the available triggers
|
||||
|
||||
New event triggers are continually being developed by SailPoint to satisfy a variety of use cases. Some of these triggers are considered **early access** and are only available in an IDN tenant upon request. To see a list of available event triggers in your tenant, navigate to the **Event Triggers** tab in the **Admin** section of IdentityNow. The first page you will see is a list of available event triggers in your tenant. You can click on each trigger to learn more about what type it is, what causes it to fire, and what the payload will look like.
|
||||
New event triggers are continually being developed by SailPoint to satisfy a variety of use cases. Some of these triggers are considered **early access** and are only available in an IDN tenant upon request. To see a list of available event triggers in your tenant, navigate to the **Event Triggers** tab in the **Admin** section of IdentityNow. The first page you will see is a list of available event triggers in your tenant. You can click on each trigger to learn more about what type it is, what causes it to fire, and what the payload will look like.
|
||||
|
||||

|
||||
|
||||
@@ -18,8 +18,8 @@ Please see [subscribing to event triggers](https://documentation.sailpoint.com/s
|
||||
|
||||
## Subscribe to a trigger from the API
|
||||
|
||||
Sometimes, you might have to use the API to subscribe to event triggers. This can occur when you want to programatically subscribe/unsubscribe from event triggers in a custom application or no code solution that doesn't have a native integration with SailPoint.
|
||||
Sometimes, you might have to use the API to subscribe to event triggers. This can occur when you want to programatically subscribe/unsubscribe from event triggers in a custom application or no code solution that doesn't have a native integration with SailPoint.
|
||||
|
||||
If this is your first time calling a SailPoint API, please follow the [getting started guide](../getting-started/index.md) to learn how to generate a token and call the APIs.
|
||||
|
||||
Start by reviewing the list of [available event triggers](/idn/api/beta/triggers#available-event-triggers), and take note of the **ID** of the trigger that you want to subscribe to (ex `idn:access-request-dynamic-approver`). Use the [create subscription](/idn/api/beta/create-subscription) endpoint to subscribe to an event trigger of your choosing. See the API docs for the latest details on how to craft a subscription request.
|
||||
Start by reviewing the list of [available event triggers](/idn/api/beta/triggers#available-event-triggers), and take note of the **ID** of the trigger that you want to subscribe to (ex `idn:access-request-dynamic-approver`). Use the [create subscription](/idn/api/beta/create-subscription) endpoint to subscribe to an event trigger of your choosing. See the API docs for the latest details on how to craft a subscription request.
|
||||
|
||||
@@ -9,43 +9,45 @@ It is important to test your trigger subscription configuration with your actual
|
||||
|
||||
## Sending test invocations
|
||||
|
||||
The easiest way to send a test event to your subscribing service is to use the **Test Subscription** command. Navigate to your subscription in the Event Trigger UI, click the options button to the right of it, and select **Test Subscription**.
|
||||
The easiest way to send a test event to your subscribing service is to use the **Test Subscription** command. Navigate to your subscription in the Event Trigger UI, click the options button to the right of it, and select **Test Subscription**.
|
||||
|
||||

|
||||
|
||||
This will send a test event to your subscribing service using the default example payload for the specific trigger you are subscribing to. This is an easy way to validate that your service can receive events, but it lacks the ability to modify the event payload to test your filter against different payloads. Fortunately, there is an API endpoint that allows you to modify the test payload.
|
||||
This will send a test event to your subscribing service using the default example payload for the specific trigger you are subscribing to. This is an easy way to validate that your service can receive events, but it lacks the ability to modify the event payload to test your filter against different payloads. Fortunately, there is an API endpoint that allows you to modify the test payload.
|
||||
|
||||
If you want to control the test input to validate your filter against a more robust set of data, then you can use the [test invocation](/idn/api/beta/start-test-invocation) API endpoint. This API allows you to send an input payload with any values that you want. An example invocation of this API is as follows:
|
||||
If you want to control the test input to validate your filter against a more robust set of data, then you can use the [test invocation](/idn/api/beta/start-test-invocation) API endpoint. This API allows you to send an input payload with any values that you want. An example invocation of this API is as follows:
|
||||
|
||||
```text
|
||||
POST `https://{tenant}.api.identitynow.com/beta/trigger-invocations/test`
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"triggerId": "idn:access-request-pre-approval",
|
||||
"input": {
|
||||
"accessRequestId": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"requestedFor": {
|
||||
"type": "IDENTITY",
|
||||
"id": "2c91808568c529c60168cca6f90c1313",
|
||||
"name": "William Wilson"
|
||||
},
|
||||
"requestedItems": [
|
||||
{
|
||||
"id": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"name": "Engineering Access",
|
||||
"description": "Access to engineering database",
|
||||
"type": "ACCESS_PROFILE",
|
||||
"operation": "Add",
|
||||
"comment": "William needs this access to do his job."
|
||||
}
|
||||
],
|
||||
"requestedBy": {
|
||||
"type": "IDENTITY",
|
||||
"id": "2c91808568c529c60168cca6f90c1314",
|
||||
"name": "Rob Robertson"
|
||||
"triggerId": "idn:access-request-pre-approval",
|
||||
"input": {
|
||||
"accessRequestId": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"requestedFor": {
|
||||
"type": "IDENTITY",
|
||||
"id": "2c91808568c529c60168cca6f90c1313",
|
||||
"name": "William Wilson"
|
||||
},
|
||||
"requestedItems": [
|
||||
{
|
||||
"id": "2c91808b6ef1d43e016efba0ce470904",
|
||||
"name": "Engineering Access",
|
||||
"description": "Access to engineering database",
|
||||
"type": "ACCESS_PROFILE",
|
||||
"operation": "Add",
|
||||
"comment": "William needs this access to do his job."
|
||||
}
|
||||
},
|
||||
"contentJson": {}
|
||||
],
|
||||
"requestedBy": {
|
||||
"type": "IDENTITY",
|
||||
"id": "2c91808568c529c60168cca6f90c1314",
|
||||
"name": "Rob Robertson"
|
||||
}
|
||||
},
|
||||
"contentJson": {}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -53,11 +55,11 @@ POST `https://{tenant}.api.identitynow.com/beta/trigger-invocations/test`
|
||||
|
||||
### Trigger service issues
|
||||
|
||||
If your subscribing service isn't receiving your test invocations, then you have a couple of options to debug the issue. Start by viewing the activity log for the subscription in the UI to make sure your test events are actually being sent.
|
||||
If your subscribing service isn't receiving your test invocations, then you have a couple of options to debug the issue. Start by viewing the activity log for the subscription in the UI to make sure your test events are actually being sent.
|
||||
|
||||

|
||||
|
||||
Check the **Created** date with the time you sent the test events. If they are being sent, check the details of the event. Look for any errors being reported, and make sure your subscribing service's subscription ID is in the `subcriptionId` that the event was sent to.
|
||||
Check the **Created** date with the time you sent the test events. If they are being sent, check the details of the event. Look for any errors being reported, and make sure your subscribing service's subscription ID is in the `subcriptionId` that the event was sent to.
|
||||
|
||||

|
||||
|
||||
@@ -65,11 +67,11 @@ You can also view the activity log using the [list latest invocation statuses](/
|
||||
|
||||
### Filter issues
|
||||
|
||||
If you don't see your events in the activity log, then it could be a filtering issue. If the filter you configured on the subscription isn't matching the test event data, then no event will be sent. Double check your filter expression with the test payload in a JSONpath editor to make sure the filter is valid and matching your data. See [Filtering Events](./filtering-events.md) for more information.
|
||||
If you don't see your events in the activity log, then it could be a filtering issue. If the filter you configured on the subscription isn't matching the test event data, then no event will be sent. Double check your filter expression with the test payload in a JSONpath editor to make sure the filter is valid and matching your data. See [Filtering Events](./filtering-events.md) for more information.
|
||||
|
||||
### Misconfigured subscription
|
||||
|
||||
Double check that your subscription configuration is correct.
|
||||
|
||||
- Make sure the URL you provided is reachable from the public internet. If your subscribing service is hosted internally in your company's intranet, then you may be able to reach it from your computer but the trigger service might not be able to.
|
||||
- Check that the authentication details are correct. Verify that the username/password or bearer token is valid.
|
||||
- Make sure the URL you provided is reachable from the public internet. If your subscribing service is hosted internally in your company's intranet, then you may be able to reach it from your computer but the trigger service might not be able to.
|
||||
- Check that the authentication details are correct. Verify that the username/password or bearer token is valid.
|
||||
|
||||
@@ -9,7 +9,7 @@ sidebar_position: 1
|
||||
|
||||
A fire and forget trigger only supports one-way communication with subscribers. Its only job is to forward each event it receives to each subscribing service. This type of trigger doesn't wait for a response from subscribers, it has no means of knowing if subscribers actually receive the event, and it doesn't have any mechanism for resending events. This type of trigger can best be thought of as live television, where you can only see what is happening in real time. There is no ability to rewind the live feed or interact with the broadcast in any way. This type of trigger is the simplest and most common trigger type among SailPoint's event triggers.
|
||||
|
||||
:::info
|
||||
:::caution
|
||||
Fire and forget triggers can have a maximum of 50 subscribers per event.
|
||||
:::
|
||||
|
||||
@@ -17,6 +17,6 @@ Fire and forget triggers can have a maximum of 50 subscribers per event.
|
||||
|
||||
A request response trigger allows two-way communication between the trigger service and the subscriber. The main difference with this trigger type is that it expects a response from the subscriber with directions on how to proceed with the event. For example, the access request dynamic approval event trigger will send the subscriber details about the access request, and the subscriber may respond to the trigger with the identity ID to include in the approval process for an access request. This type of trigger allows subscribers to not just receive events in real-time, but act on them as well.
|
||||
|
||||
:::info
|
||||
:::caution
|
||||
Request response triggers can only have one subscriber per event.
|
||||
:::
|
||||
|
||||
@@ -1,463 +0,0 @@
|
||||
---
|
||||
id: authentication
|
||||
---
|
||||
# Authentication
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to authenticate and start using SailPoint APIs is to generate a [personal access token](#personal-access-tokens). If you are interested in using OAuth2 for authentication, then please continue to read this document.
|
||||
|
||||
## Finding Your Tenant's OAuth Details
|
||||
|
||||
This document assumes your IDN instance is using the domain name supplied by SailPoint. If your instance is using a vanity URL, then you will need to open the following URL in your browser to get your OAuth info. See [finding your org/tenant name](./index.md#finding-your-orgtenant-name) in the [getting started guide](./index.md) to get your `{tenant}`.
|
||||
|
||||
`https://{tenant}.api.identitynow.com/oauth/info`
|
||||
|
||||
This page will present you with your `authorizeEndpoint` and `tokenEndpoint`, which you will need to follow along with the examples in this document.
|
||||
|
||||
```json
|
||||
{
|
||||
"tenantId": "cc31a307-8a8d-49e8-93b9-c7cbe20e2e6b",
|
||||
"tenantName": "iga-acme-sb",
|
||||
"authorizeEndpoint": "https://iga-sb.acme.com/oauth/authorize",
|
||||
"tokenEndpoint": "https://iga-sb.api.identitynow.com/oauth/token",
|
||||
"cloudDomainUrl": "https://iga-sb.acme.com",
|
||||
"logoutUrl": "https://iga-sb.acme.com/logout",
|
||||
"pod": "stg01-useast1"
|
||||
}
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
In order to use the IdentityNow REST API, you must first authenticate with IdentityNow and get an `access_token`. This `access_token` will need to be provided in the `Authorization` header of each API request. The steps of the flow are as follows:
|
||||
|
||||

|
||||
|
||||
1. **Access Token Request** - The HTTP client (a script, application, Postman, cURL, etc.) makes a request to IdentityNow to get an `access_token`. The details of this are described in the [Authentication Details](#authentication-details) section.
|
||||
2. **Access Token Response** - Assuming the request is valid, IdentityNow will issue an `access_token` to the HTTP client in response.
|
||||
3. **API Request** - The HTTP client makes a request to an IdentityNow API endpoint. Included in that request is the header `Authorization: Bearer {access_token}`.
|
||||
4. **API Response** - Assuming the request and the `access_token` are valid, IdentityNow will return a response to the client. If unexpected errors occur, see the [Troubleshooting](#troubleshooting) section of this document.
|
||||
|
||||
The SailPoint authentication/authorization model is fully [OAuth 2.0](https://oauth.net/2/) compliant, with issued `access_tokens` leveraging the [JSON Web Token (JWT)](https://jwt.io/) standard. This document provides the necessary information for interacting with SailPoint's OAuth2 services.
|
||||
|
||||
## Personal Access Tokens
|
||||
|
||||
A personal access token is a method of authenticating to an API as a user without needing to supply a username and password. The primary use case for personal access tokens is in scripts or programs that don't have an easy way to implement an OAuth 2.0 flow and that need to call API endpoints that require a user context. Personal access tokens are also convenient when using Postman to explore and test APIs.
|
||||
|
||||
>**UPDATE**: Previously, only users with the `Admin` or `Source Admin` role were allowed to generate personal access tokens. Now, all users are able to generate personal access tokens!
|
||||
|
||||
To generate a personal access token from the IdentityNow UI, perform the following steps after logging into your IdentityNow instance:
|
||||
|
||||
1. Select **Preferences** from the drop-down menu under your username, then **Personal Access Tokens** on the left. You can also go straight to the page using this URL, replacing `{tenant}` with your IdentityNow tenant: `https://{tenant}.identitynow.com/ui/d/user-preferences/personal-access-tokens`.
|
||||
|
||||
2. Click **New Token** and enter a meaningful description to help differentiate the token from others.
|
||||
|
||||
>**Note**: The **New Token** button will be disabled when you’ve reached the limit of 10 personal access tokens per user. To avoid reaching this limit, we recommend you delete any tokens that are no longer needed.
|
||||
|
||||
3. Click **Create Token** to generate and view the two components that comprise the token: the `Secret` and the `Client ID`.
|
||||
|
||||
>**IMPORTANT**: After you create the token, the value of the `Client ID` will be visible in the Personal Access Tokens list, but the corresponding `Secret` will not be visible after you close the window. You will need to store the `Secret` somewhere secure.
|
||||
|
||||
4. Copy both values somewhere that will be secure and accessible to you when you need to use the the token.
|
||||
|
||||
To generate a personal access token from the API, use the [create personal access token endpoint](/idn/api/beta/create-personal-access-token).
|
||||
|
||||
To use a personal access token to generate an `access_token` that can be used to authenticate requests to the API, follow the [Client Credentials Grant Flow](#client-credentials-grant-flow), using the `Client ID` and `Client Secret` obtained from the personal access token.
|
||||
|
||||
## OAuth 2.0
|
||||
|
||||
[OAuth 2.0](https://oauth.net/2/) is an industry-standard protocol for authorization, and provides a variety of authorization flows for web applications, desktop applications, mobile phones, and devices. This specification and its extensions are developed within the [IETF OAuth Working Group](https://www.ietf.org/mailman/listinfo/oauth).
|
||||
|
||||
There are several different authorization flows that OAuth 2.0 supports, and each of these has a grant-type which defines the different use cases. Some of the common ones which might be used with IdentityNow are as follows:
|
||||
|
||||
1. [**Authorization Code**](https://oauth.net/2/grant-types/authorization-code/) - This grant type is used by clients to exchange an authorization code for an `access_token`. This is mainly used for web applications as there is a login into IdentityNow, with a subsequent redirect back to the web application / client.
|
||||
2. [**Client Credentials**](https://oauth.net/2/grant-types/client-credentials/) - This grant type is used by clients to obtain an `access_token` outside the context of a user. Because this is outside of a user context, only a subset of IdentityNow REST APIs may be accessible with this kind of grant type.
|
||||
3. [**Refresh Token**](https://oauth.net/2/grant-types/refresh-token/) - This grant type is used by clients in order to exchange a refresh token for a new `access_token` when the existing `access_token` has expired. This allows clients to continue using the API without having to re-authenticate as frequently. This grant type is commonly used together with `Authorization Code` to prevent a user from having to log in several times per day.
|
||||
|
||||
## JSON Web Token (JWT)
|
||||
|
||||
[JSON Web Token (JWT)](https://jwt.io) is an industry-standard protocol for creating access tokens which assert various claims about the resource who has authenticated. The tokens have a specific structure consisting of a header, payload, and signature.
|
||||
|
||||
A raw JWT might look like this:
|
||||
|
||||
```text
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRfaWQiOiI1OGViMDZhNC1kY2Q3LTRlOTYtOGZhYy1jY2EyYWZjMDNlNjEiLCJpbnRlcm5hbCI6dHJ1ZSwicG9kIjoiY29vayIsIm9yZyI6ImV4YW1wbGUiLCJpZGVudGl0eV9pZCI6ImZmODA4MTgxNTVmZThjMDgwMTU1ZmU4ZDkyNWIwMzE2IiwidXNlcl9uYW1lIjoic2xwdC5zZXJ2aWNlcyIsInN0cm9uZ19hdXRoIjp0cnVlLCJhdXRob3JpdGllcyI6WyJPUkdfQURNSU4iXSwiY2xpZW50X2lkIjoibktCUE93akpIOExYU2pJbCIsInN0cm9uZ19hdXRoX3N1cHBvcnRlZCI6dHJ1ZSwidXNlcl9pZCI6IjU5NTgyNiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE1NjU4ODgzMTksImp0aSI6ImM5OGQxMjM2LTQ1MTMtNGM4OS1hMGQwLTBjYjlmMzI3NmI1NiJ9.SAY4ZQkXGi2cY_qz57Ah9_zDq4-bnF-oDJKotXa-LCY
|
||||
```
|
||||
|
||||
If you were to decode the access token data, it might look something like this:
|
||||
|
||||
Header
|
||||
|
||||
```JSON
|
||||
{
|
||||
"alg": "HS256",
|
||||
"typ": "JWT"
|
||||
}
|
||||
```
|
||||
|
||||
Payload
|
||||
|
||||
```JSON
|
||||
{
|
||||
"tenant_id": "58eb06a4-dcd7-4e96-8fac-cca2afc03e61",
|
||||
"internal": true,
|
||||
"pod": "cook",
|
||||
"org": "example",
|
||||
"identity_id": "ff80818155fe8c080155fe8d925b0316",
|
||||
"user_name": "slpt.services",
|
||||
"strong_auth": true,
|
||||
"authorities": [
|
||||
"ORG_ADMIN"
|
||||
],
|
||||
"client_id": "nKBPOwjJH8LXSjIl",
|
||||
"strong_auth_supported": true,
|
||||
"user_id": "595826",
|
||||
"scope": [
|
||||
"read",
|
||||
"write"
|
||||
],
|
||||
"exp": 1565888319,
|
||||
"jti": "c98d1236-4513-4c89-a0d0-0cb9f3276b56"
|
||||
}
|
||||
```
|
||||
|
||||
Signature
|
||||
|
||||
```TEXT
|
||||
HMACSHA256(
|
||||
base64UrlEncode(header) + "." +
|
||||
base64UrlEncode(payload),
|
||||
{secret}
|
||||
)
|
||||
```
|
||||
|
||||
You can check the JWT access token data online at [jwt.io](https://jwt.io).
|
||||
|
||||
## Authentication Details
|
||||
|
||||
This section details how to call the SailPoint Platform OAuth 2.0 token endpoints to get an `access_token`.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before any OAuth 2.0 token requests can be initiated, a Client ID and secret are necessary. As an `ORG_ADMIN`, browse to your API Management Admin Page at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` and create an API client with the appropriate grant types for your use case. If you are not an admin of your org, you can ask an admin to create this for you. Be sure to save your `Client Secret` somewhere secure, as you will not be able to view or change it later.
|
||||
|
||||
### OAuth 2.0 Token Request
|
||||
|
||||
When authenticating to IdentityNow, the OAuth 2.0 token endpoint resides on the IdentityNow API Gateway at:
|
||||
|
||||
```Text
|
||||
POST https://{tenant}.api.identitynow.com/oauth/token
|
||||
```
|
||||
|
||||
How you call this endpoint to get your token depends largely on the OAuth 2.0 flow and grant type you wish to implement. The details for each grant type within IdentityNow are described in the following sections.
|
||||
|
||||
### Authorization Code Grant Flow
|
||||
|
||||
Further Reading: [https://oauth.net/2/grant-types/authorization-code/](https://oauth.net/2/grant-types/authorization-code/)
|
||||
|
||||
This grant type is used by clients to exchange an authorization code for an `access_token`. This is mainly used for web apps as there is a login into IdentityNow, with a subsequent redirect back to the web app / client.
|
||||
|
||||
The OAuth 2.0 client you are using must have `AUTHORIZATION_CODE` as one of its grant types. The redirect URLs must also match the list in the client as well:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"created": "2019-05-23T02:06:20.685Z",
|
||||
"name": "My Application",
|
||||
"description": "My Application",
|
||||
"id": "b61429f5-203d-494c-94c3-04f54e17bc5c",
|
||||
"secret": null,
|
||||
"grantTypes": [
|
||||
"AUTHORIZATION_CODE"
|
||||
],
|
||||
"redirectUris": [
|
||||
"http://localhost:8080/myApp/code"
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The overall authorization flow is as follows:
|
||||
|
||||

|
||||
|
||||
1. The user clicks the login link on a web app.
|
||||
|
||||
2. The web app sends an authorization request to IdentityNow in the form:
|
||||
|
||||
```Text
|
||||
GET https://{tenant}.identitynow.com/oauth/authorize?client_id={client-id}&client_secret={client-secret}&response_type=code&redirect_uri={redirect-url}
|
||||
```
|
||||
|
||||
3. IdentityNow redirects the user to a login prompt to authenticate to IdentityNow.
|
||||
|
||||
4. The user authenticates to IdentityNow.
|
||||
|
||||
5. Once authentication is successful, IdentityNow issues an authorization code back to the web app.
|
||||
|
||||
6. The web app submits an **OAuth 2.0 Token Request** to IdentityNow in the form:
|
||||
|
||||
```text
|
||||
POST https://{tenant}.api.identitynow.com/oauth/token?grant_type=authorization_code&client_id={client-id}&client_secret={client-secret}&code={code}&redirect_uri={redirect-url}
|
||||
```
|
||||
|
||||
>**Note**: the token endpoint URL is `{tenant}.api.identitynow.com`, while the authorize URL is `{tenant}.identitynow.com`. Be sure to use the correct URL when setting up your webapp to use this flow.
|
||||
|
||||
7. IdentityNow validates the token request and submits a response. If successful, the response will contain a JWT `access_token`.
|
||||
|
||||
The query parameters in the OAuth 2.0 token request for the Authorization Code grant are as follows:
|
||||
|
||||
| Key | Description |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| grant_type | Set to `authorization_code` for the authorization code grant type. |
|
||||
| client_id | This is the client ID for the API client (e.g. `b61429f5-203d-494c-94c3-04f54e17bc5c`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` |
|
||||
| client_secret | This is the client secret for the API client (e.g. `c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` |
|
||||
| code | This is a code returned by `/oauth/authorize`. |
|
||||
| redirect_uri | This is a URL of the application to redirect to once the token has been granted. |
|
||||
|
||||
Here is an example OAuth 2.0 token request for the Authorization Code grant type.
|
||||
|
||||
```sh
|
||||
curl -X POST \
|
||||
'https://example.api.identitynow.com/oauth/token?grant_type=authorization_code&client_id=b61429f5-203d-494c-94c3-04f54e17bc5c&client_secret=c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5&code=6688LQJB0y652z6ZjFmkCKuBUjv2sTIqKS2JthWrZ7qlPgI9TClJ6FnpweEhO6w7&redirect_uri=https://myappdomain.com/oauth/redirect' \
|
||||
-H 'cache-control: no-cache'
|
||||
```
|
||||
|
||||
### Client Credentials Grant Flow
|
||||
|
||||
Further Reading: [https://oauth.net/2/grant-types/client-credentials/](https://oauth.net/2/grant-types/client-credentials/)
|
||||
|
||||
This grant type is used by clients to obtain an access token outside the context of a user. This is probably the simplest authentication flow, but comes with a major drawback; API endpoints that require [user level permissions](https://documentation.sailpoint.com/saas/help/common/users/user_level_matrix.html) will not work. [Personal Access Tokens](#personal-access-tokens) are a form of Client Credentials that have a user context, so they do not share this drawback. However, the APIs that can be invoked with a personal access token depend on the permissions of the user that generated it.
|
||||
|
||||
An OAuth 2.0 client using the Client Credentials flow must have `CLIENT_CREDENTIALS` as one of its grantTypes:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"created": "2019-05-23T02:06:20.685Z",
|
||||
"name": "My Application",
|
||||
"description": "My Application",
|
||||
"id": "b61429f5-203d-494c-94c3-04f54e17bc5c",
|
||||
"secret": null,
|
||||
"grantTypes": [
|
||||
"CLIENT_CREDENTIALS"
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
[Personal Access Tokens](#personal-access-tokens) are implicly granted a `CLIENT_CREDENTIALS` grant type.
|
||||
|
||||
The overall authorization flow looks like this:
|
||||
|
||||
1. The client submits an **OAuth 2.0 Token Request** to IdentityNow in the form:
|
||||
|
||||
```Text
|
||||
POST https://{tenant}.api.identitynow.com/oauth/token?grant_type=client_credentials&client_id={client-id}&client_secret={client-secret}
|
||||
```
|
||||
|
||||
2. IdentityNow validates the token request and submits a response. If successful, the response will contain a JWT access token.
|
||||
|
||||
The query parameters in the OAuth 2.0 Token Request for the Client Credentials grant are as follows:
|
||||
|
||||
| Key | Description |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| grant_type | Set to `CLIENT_CREDENTIALS` for the authorization code grant type. |
|
||||
| client_id | This is the client ID describing for the API client (e.g. `b61429f5-203d-494c-94c3-04f54e17bc5c`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` or by [creating a personal access token](#personal-access-tokens). |
|
||||
| client_secret | This is the client secret describing for the API client (e.g. `c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` or by [creating a personal access token](#personal-access-tokens). |
|
||||
|
||||
Here is an example request to generate an `access_token` using Client Credentials.
|
||||
|
||||
```sh
|
||||
curl -X POST \
|
||||
'https://{tenant}.api.identitynow.com/oauth/token?grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}' \
|
||||
-H 'cache-control: no-cache'
|
||||
```
|
||||
|
||||
### Refresh Token Grant Flow
|
||||
|
||||
Further Reading: [https://oauth.net/2/grant-types/refresh-token/](https://oauth.net/2/grant-types/refresh-token/)
|
||||
|
||||
This grant type is used by clients in order to exchange a refresh token for a new `access_token` once the existing `access_token` has expired. This allows clients to continue to have a valid `access_token` without the need for the user to login as frequently.
|
||||
|
||||
The OAuth 2.0 client you are using must have `REFRESH_TOKEN` as one of its grant types, and is typically used in conjunction with another grant type, like `CLIENT_CREDENTIALS` or `AUTHORIZATION_CODE`:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"created": "2019-05-23T02:06:20.685Z",
|
||||
"name": "My Application",
|
||||
"description": "My Application",
|
||||
"id": "b61429f5-203d-494c-94c3-04f54e17bc5c",
|
||||
"secret": null,
|
||||
"grantTypes": [
|
||||
"REFRESH_TOKEN",
|
||||
"AUTHORIZATION_CODE"
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The overall authorization flow looks like this:
|
||||
|
||||
1. The client application receives an `access_token` and a `refresh_token` via one of the other OAuth grant flows, like `AUTHORIZATION_CODE`.
|
||||
2. The client application notices that the `access_token` is about to expire, based on the `expires_in` attribute contained within the JWT token.
|
||||
3. The client submits an **OAuth 2.0 Token Request** to IdentityNow in the form:
|
||||
|
||||
```Text
|
||||
POST https://{tenant}.api.identitynow.com/oauth/token?grant_type=refresh_token&client_id={client_id}&client_secret={client_secret}&refresh_token={refresh_token}
|
||||
```
|
||||
|
||||
4. IdentityNow validates the token request and submits a response. If successful, the response will contain a new `access_token` and `refresh_token`.
|
||||
|
||||
The query parameters in the OAuth 2.0 Token Request for the Refresh Token grant are as follows:
|
||||
|
||||
| Key | Description |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| grant_type | Set to `refresh_token` for the authorization code grant type. |
|
||||
| client_id | This is the client ID for the API client (e.g. `b61429f5-203d-494c-94c3-04f54e17bc5c`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel`. |
|
||||
| client_secret | This is the client secret for the API client (e.g. `c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel`. |
|
||||
| refresh_token | This is the `refresh_token` that was provided along with the now expired `access_token`. |
|
||||
|
||||
Here is an example call OAuth 2.0 Token Request for the Refresh Token grant.
|
||||
|
||||
```sh
|
||||
curl -X POST \
|
||||
'https://example.api.identitynow.com/oauth/token?grant_type=refresh_token&client_id=b61429f5-203d-494c-94c3-04f54e17bc5c&client_secret=c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5&refresh_token=ey...4M' \
|
||||
-H 'cache-control: no-cache'
|
||||
```
|
||||
|
||||
## OAuth 2.0 Token Response
|
||||
|
||||
A successful request to `https://{tenant}.api.identitynow.com/oauth/token` will contain a response body similar to this:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRfaWQiOiI1OGViMDZhNC1kY2Q3LTRlOTYtOGZhYy1jY2EyYWZjMDNlNjEiLCJpbnRlcm5hbCI6ZmFsc2UsInBvZCI6ImNvb2siLCJvcmciOiJuZWlsLXRlc3QiLCJpZGVudGl0eV9pZCI6ImZmODA4MTgxNTVmZThjMDgwMTU1ZmU4ZDkyNWIwMzE2IiwidXNlcl9uYW1lIjoic2xwdC5zZXJ2aWNlcyIsInN0cm9uZ19hdXRoIjp0cnVlLCJhdXRob3JpdGllcyI6WyJPUkdfQURNSU4iXSwiZW5hYmxlZCI6dHJ1ZSwiY2xpZW50X2lkIjoiZmNjMGRkYmItMTA1Yy00Y2Q3LWI5NWUtMDI3NmNiZTQ1YjkwIiwiYWNjZXNzVHlwZSI6Ik9GRkxJTkUiLCJzdHJvbmdfYXV0aF9zdXBwb3J0ZWQiOmZhbHNlLCJ1c2VyX2lkIjoiNTk1ODI2Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTU2NTg5MTA2MywianRpIjoiOTQ5OWIyOTktOTVmYS00N2ZiLTgxNWMtODVkNWY2YjQzZTg2In0.zJYfjIladuGHoLXr92EOJ3A9qGNkiG5UJ9eqrtSYXAQ",
|
||||
"token_type": "bearer",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRfaWQiOiI1OGViMDZhNC1kY2Q3LTRlOTYtOGZhYy1jY2EyYWZjMDNlNjEiLCJpbnRlcm5hbCI6ZmFsc2UsInBvZCI6ImNvb2siLCJvcmciOiJuZWlsLXRlc3QiLCJpZGVudGl0eV9pZCI6ImZmODA4MTgxNTVmZThjMDgwMTU1ZmU4ZDkyNWIwMzE2IiwidXNlcl9uYW1lIjoic2xwdC5zZXJ2aWNlcyIsInN0cm9uZ19hdXRoIjp0cnVlLCJhdXRob3JpdGllcyI6WyJPUkdfQURNSU4iXSwiZW5hYmxlZCI6dHJ1ZSwiY2xpZW50X2lkIjoiZmNjMGRkYmItMTA1Yy00Y2Q3LWI5NWUtMDI3NmNiZTQ1YjkwIiwiWYNjZXNzVHlwZSI6Ik9GRkxJTkUiLCJzdHJvbmdfYXV0aF9zdXBwb3J0ZWQiOmZhbHNlLCJ1c2VyX2lkIjoiNTk1ODI2Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6Ijk0OTliMjk5LTk1ZmEtNDdmYi04MTVjLTg1ZDVmNmI0M2U4NiIsImV4cCI6MTU2NTk3NjcxMywianRpIjoiODliODk1ZDMtNTdlNC00ZDAwLWI5ZjctOTFlYWVjNDcxMGQ3In0.pfDcB0sGChdHk-oDNmiIxsKFLxq9CcPQV5-eXWgIcp4",
|
||||
"expires_in": 749,
|
||||
"scope": "read write",
|
||||
"accessType": "OFFLINE",
|
||||
"tenant_id": "58eb06a4-dcd7-4e96-8fac-cca2afc03e61",
|
||||
"internal": false,
|
||||
"pod": "cook",
|
||||
"strong_auth_supported": false,
|
||||
"org": "example",
|
||||
"user_id": "595826",
|
||||
"identity_id": "ff80818155fe8c080155fe8d925b0316",
|
||||
"strong_auth": true,
|
||||
"enabled": true,
|
||||
"jti": "9499b299-95fa-47fb-815c-85d5f6b43e86"
|
||||
}
|
||||
```
|
||||
|
||||
The `access_token` contains the JSON Web Token which is subsequently used in any further REST API calls through the IdentityNow API gateway. To use the `access_token`, simply include it in the `Authorization` header as a `Bearer` token. For example:
|
||||
|
||||
```sh
|
||||
curl -X GET \
|
||||
'https://{tenant}.api.identitynow.com/v3/account-activities' \
|
||||
-H 'Authorization: Bearer {access_token}' \
|
||||
-H 'cache-control: no-cache'
|
||||
```
|
||||
|
||||
The `expires_in` describes the lifetime, in seconds, of the `access_token`. For example, the value 749 means that the `access_token` will expire in 12.5 minutes from the time the response was generated. The exact expiration date is also contained within the `access_token`. You can view this expiration time by decoding the JWT `access_token` using a tool like [jwt.io](https://jwt.io/).
|
||||
|
||||
The `refresh_token` contains a JSON Web Token for use in a [Refresh Token](#refresh-token-grant-flow) grant flow. The `refresh_token` will only be present if the API client has the `REFRESH_CODE` grant flow.
|
||||
|
||||
The `user_id` and `identity_id` define the identity context of the person that authenticated. This is not set for the Client Credentials grant type since it doesn't have a user context.
|
||||
|
||||
## Which OAuth 2.0 Grant Flow should I use?
|
||||
|
||||
Deciding which OAuth 2.0 grant flow you should use largely depends on your use case.
|
||||
|
||||
### Daily Work or Quick Actions
|
||||
|
||||
For daily work or short, quick administrative actions, you may not really need to worry about grant types, as an access token can easily be obtained in the user interface. In order to see this:
|
||||
|
||||
1. Login to IdentityNow.
|
||||
2. Go to `https://{tenant}.identitynow.com/ui/session`.
|
||||
3. The `accessToken` is visible in the user interface.
|
||||
4. Use this access token in the `Authorization` header when making API calls. If the access token expires, log back into Identity Now and retrieve the new access token.
|
||||
|
||||
While this is very simple to use, this is only valid for a short period of time (a few minutes).
|
||||
|
||||
### Postman
|
||||
|
||||
If you are using the popular HTTP client, [Postman](https://www.getpostman.com), you have a couple of options on how you might setup your authorization. You can just leverage the accessToken as mentioned above, or you can also configure Postman to use OAuth 2.0 directly.
|
||||
|
||||
### Web Applications
|
||||
|
||||
If you are making a web application, the best grant flow to use is the [Authorization Code](#authorization-code-grant-flow) grant flow. This will allow users to be directed to IdentityNow to login, and then redirected back to the web application via a URL redirect. This also works well with SSO, strong authentication, or pass-through authentication mechanisms.
|
||||
|
||||
SailPoint does not recommend using a password grant flow for web applications as it would involve entering IdentityNow credentials in the web application. This flow also doesn't allow you to work with SSO, strong authentication, or pass-through authentication.
|
||||
|
||||
### Scripts or Programs
|
||||
|
||||
If you are writing scripts or programs that leverage the IdentityNow APIs, which OAuth 2.0 grant from you should use typically depends on what you are doing, and which user context you need to operate under.
|
||||
|
||||
Because scripts, code, or programs do not have an interactive web-interface it is difficult, but not impossible, to implement a working [Authorization Code](#authorization-code-grant-flow) flow. Most scripts or programs typically run as a [Client Credentials](#client-credentials-grant-flow). If your APIs can work under an API context without a user, then [Client Credentials](#client-credentials-grant-flow) is ideal. However, if your APIs need a user or admin context, then the [Personal Access Token](#personal-access-tokens) approach will be more suitable.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Having issues? Follow these steps.
|
||||
|
||||
1. **Verify the API End Point Calls**
|
||||
|
||||
1. Verify the structure of the API call:
|
||||
1. Verify that the API calls are going through the API gateway:
|
||||
`https://{tenant}.api.identitynow.com`
|
||||
2. Verify you are calling their version correctly:
|
||||
- Private APIs: `https://{tenant}.api.identitynow.com/cc/api/{endpoint}`
|
||||
- V2 APIs: `https://{tenant}.api.identitynow.com/v2/{endpoint}`
|
||||
- V3 APIs: `https://{tenant}.api.identitynow.com/v3/{endpoint}`
|
||||
- Beta APIs: `https://{tenant}.api.identitynow.com/beta/{endpoint}`
|
||||
3. Verify that the API calls have the correct headers (e.g., `content-type`), query parameters, and body data.
|
||||
2. If the HTTP response is **401 Unauthorized** , this is an indication that either there is no `Authorization` header or the `access_token` is invalid. Verify that the API calls are supplying the `access_token` in the `Authorization` header correctly (ex. `Authorization: Bearer {access_token}`) and that the `access_token` has not expired.
|
||||
3. If the HTTP response is **403 Forbidden**, this is an indication that the `access_token` is valid, but the user you are running as doesn't have access to this endpoint. Check the access rights which are associated with the user.
|
||||
>**Note**: This can also be due to calling an API which expects a user, but your authorization grant type might not have a user context. Calling most administrative APIs with a CLIENT_CREDENTIAL grant will often produce this result.
|
||||
|
||||
2. **Verify the OAuth 2.0 Client**
|
||||
|
||||
1. Verify that the OAuth 2.0 Client is not a Legacy OAuth client. Legacy OAuth clients will not work.
|
||||
This is very apparent by looking at the Client ID, as OAuth 2.0 Client IDs have dashes. Here is an example:
|
||||
Legacy Client ID: `G6xLlBBOKIcOAQuK`
|
||||
OAuth 2.0 Client ID: `b61429f5-203d-494c-94c3-04f54e17bc5c`
|
||||
|
||||
2. Verify the OAuth 2.0 Client ID exists. This can be verified by calling:
|
||||
|
||||
```text
|
||||
GET /beta/oauth-clients/{client-id}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```text
|
||||
GET /beta/oauth-clients/
|
||||
```
|
||||
|
||||
You can also view all of the active clients in the UI by going to `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel`.
|
||||
|
||||
3. Verify that the OAuth 2.0 Client grant types match the OAuth 2.0 grant type flow you are trying to use. For instance, this client will work with [Authorization Code](#authorization-code-grant-flow) and [Client Credentials](#client-Credentials-grant-flow) flows, but not [Refresh Token](#refresh-token-grant-flow) flows:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"created": "2019-05-23T02:06:20.685Z",
|
||||
"name": "My Application",
|
||||
"description": "My Application",
|
||||
"id": "b61429f5-203d-494c-94c3-04f54e17bc5c",
|
||||
"secret": null,
|
||||
"grantTypes": [
|
||||
"AUTHORIZATION_CODE",
|
||||
"CLIENT_CREDENTIALS"
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
4. If using an A[Authorization Code](#authorization-code-grant-flow) flow, verify the redirect URL(s) for your application match the `redirectUris` value in the client. You can check this using the [oauth-clients endpoint](/idn/api/beta/list-oauth-client).
|
||||
|
||||
3. **Verify the OAuth 2.0 Calls**
|
||||
|
||||
1. Verify that the OAuth call flow is going to the right URLs, with the correct query parameters and data values. A common source of errors is using the wrong host for authorization and token API calls. The token endpoint URL is `{tenant}.api.identitynow.com`, while the authorize URL is `{tenant}.identitynow.com`.
|
||||
474
products/idn/docs/getting-started/authentication.mdx
Normal file
474
products/idn/docs/getting-started/authentication.mdx
Normal file
@@ -0,0 +1,474 @@
|
||||
---
|
||||
id: authentication
|
||||
---
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Authentication
|
||||
|
||||
## Overview
|
||||
|
||||
The quickest way to authenticate and start using SailPoint APIs is to generate a [personal access token](#personal-access-tokens). If you are interested in using OAuth2 for authentication, then please continue to read this document.
|
||||
|
||||
In order to use the IdentityNow REST API, you must first authenticate with IdentityNow and get an `access_token`. This `access_token` will need to be provided in the `Authorization` header of each API request. The steps of the flow are as follows:
|
||||
|
||||

|
||||
|
||||
1. **Access Token Request** - The HTTP client (a script, application, Postman, cURL, etc.) makes a request to IdentityNow to get an `access_token`. The details of this are described in the [Authentication Details](#authentication-details) section.
|
||||
2. **Access Token Response** - Assuming the request is valid, IdentityNow will issue an `access_token` to the HTTP client in response.
|
||||
3. **API Request** - The HTTP client makes a request to an IdentityNow API endpoint. Included in that request is the header `Authorization: Bearer {access_token}`.
|
||||
4. **API Response** - Assuming the request and the `access_token` are valid, IdentityNow will return a response to the client. If unexpected errors occur, see the [Troubleshooting](#troubleshooting) section of this document.
|
||||
|
||||
The SailPoint authentication/authorization model is fully [OAuth 2.0](https://oauth.net/2/) compliant, with issued `access_tokens` leveraging the [JSON Web Token (JWT)](https://jwt.io/) standard. This document provides the necessary information for interacting with SailPoint's OAuth2 services.
|
||||
|
||||
## Find Your Tenant's OAuth Details
|
||||
|
||||
This document assumes your IDN instance is using the domain name supplied by SailPoint. If your instance is using a vanity URL, then you will need to open the following URL in your browser to get your OAuth info. See [finding your org/tenant name](./index.md#finding-your-orgtenant-name) in the [getting started guide](./index.md) to get your `{tenant}`.
|
||||
|
||||
`https://{tenant}.api.identitynow.com/oauth/info`
|
||||
|
||||
This page will present you with your `authorizeEndpoint` and `tokenEndpoint`, which you will need to follow along with the examples in this document.
|
||||
|
||||
```json
|
||||
{
|
||||
"tenantId": "cc31a307-8a8d-49e8-93b9-c7cbe20e2e6b",
|
||||
"tenantName": "iga-acme-sb",
|
||||
"authorizeEndpoint": "https://iga-sb.acme.com/oauth/authorize",
|
||||
"tokenEndpoint": "https://iga-sb.api.identitynow.com/oauth/token",
|
||||
"cloudDomainUrl": "https://iga-sb.acme.com",
|
||||
"logoutUrl": "https://iga-sb.acme.com/logout",
|
||||
"pod": "stg01-useast1"
|
||||
}
|
||||
```
|
||||
|
||||
## Personal Access Tokens
|
||||
|
||||
A personal access token is a method of authenticating to an API as a user without needing to supply a username and password. The primary use case for personal access tokens is in scripts or programs that don't have an easy way to implement an OAuth 2.0 flow and that need to call API endpoints that require a user context. Personal access tokens are also convenient when using Postman to explore and test APIs.
|
||||
|
||||
:::info Update
|
||||
Previously, only users with the `Admin` or `Source Admin` role were allowed to generate personal access tokens. Now, all users are able to generate personal access tokens!
|
||||
:::
|
||||
|
||||
To generate a personal access token from the IdentityNow UI, perform the following steps after logging into your IdentityNow instance:
|
||||
|
||||
1. Select **Preferences** from the drop-down menu under your username, then **Personal Access Tokens** on the left. You can also go straight to the page using this URL, replacing `{tenant}` with your IdentityNow tenant: `https://{tenant}.identitynow.com/ui/d/user-preferences/personal-access-tokens`.
|
||||
|
||||
2. Click **New Token** and enter a meaningful description to help differentiate the token from others.
|
||||
|
||||
:::note
|
||||
The **New Token** button will be disabled when you’ve reached the limit of 10 personal access tokens per user. To avoid reaching this limit, we recommend you delete any tokens that are no longer needed.
|
||||
:::
|
||||
|
||||
3. Click **Create Token** to generate and view the two components that comprise the token: the `Secret` and the `Client ID`.
|
||||
|
||||
:::danger Important
|
||||
After you create the token, the value of the `Client ID` will be visible in the Personal Access Tokens list, but the corresponding `Secret` will not be visible after you close the window. You will need to store the `Secret` somewhere secure.
|
||||
:::
|
||||
|
||||
4. Copy both values somewhere that will be secure and accessible to you when you need to use the the token.
|
||||
|
||||
To generate a personal access token from the API, use the [create personal access token endpoint](/idn/api/beta/create-personal-access-token).
|
||||
|
||||
To use a personal access token to generate an `access_token` that can be used to authenticate requests to the API, follow the [Client Credentials Grant Flow](#client-credentials-grant-flow), using the `Client ID` and `Client Secret` obtained from the personal access token.
|
||||
|
||||
## OAuth 2.0
|
||||
|
||||
[OAuth 2.0](https://oauth.net/2/) is an industry-standard protocol for authorization, and provides a variety of authorization flows for web applications, desktop applications, mobile phones, and devices. This specification and its extensions are developed within the [IETF OAuth Working Group](https://www.ietf.org/mailman/listinfo/oauth).
|
||||
|
||||
There are several different authorization flows that OAuth 2.0 supports, and each of these has a grant-type which defines the different use cases. Some of the common ones which might be used with IdentityNow are as follows:
|
||||
|
||||
1. [**Authorization Code**](https://oauth.net/2/grant-types/authorization-code/) - This grant type is used by clients to exchange an authorization code for an `access_token`. This is mainly used for web applications as there is a login into IdentityNow, with a subsequent redirect back to the web application / client.
|
||||
2. [**Client Credentials**](https://oauth.net/2/grant-types/client-credentials/) - This grant type is used by clients to obtain an `access_token` outside the context of a user. Because this is outside of a user context, only a subset of IdentityNow REST APIs may be accessible with this kind of grant type.
|
||||
3. [**Refresh Token**](https://oauth.net/2/grant-types/refresh-token/) - This grant type is used by clients in order to exchange a refresh token for a new `access_token` when the existing `access_token` has expired. This allows clients to continue using the API without having to re-authenticate as frequently. This grant type is commonly used together with `Authorization Code` to prevent a user from having to log in several times per day.
|
||||
|
||||
## JSON Web Token (JWT)
|
||||
|
||||
[JSON Web Token (JWT)](https://jwt.io) is an industry-standard protocol for creating access tokens which assert various claims about the resource who has authenticated. The tokens have a specific structure consisting of a header, payload, and signature.
|
||||
|
||||
A raw JWT might look like this:
|
||||
|
||||
```text
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRfaWQiOiI1OGViMDZhNC1kY2Q3LTRlOTYtOGZhYy1jY2EyYWZjMDNlNjEiLCJpbnRlcm5hbCI6dHJ1ZSwicG9kIjoiY29vayIsIm9yZyI6ImV4YW1wbGUiLCJpZGVudGl0eV9pZCI6ImZmODA4MTgxNTVmZThjMDgwMTU1ZmU4ZDkyNWIwMzE2IiwidXNlcl9uYW1lIjoic2xwdC5zZXJ2aWNlcyIsInN0cm9uZ19hdXRoIjp0cnVlLCJhdXRob3JpdGllcyI6WyJPUkdfQURNSU4iXSwiY2xpZW50X2lkIjoibktCUE93akpIOExYU2pJbCIsInN0cm9uZ19hdXRoX3N1cHBvcnRlZCI6dHJ1ZSwidXNlcl9pZCI6IjU5NTgyNiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE1NjU4ODgzMTksImp0aSI6ImM5OGQxMjM2LTQ1MTMtNGM4OS1hMGQwLTBjYjlmMzI3NmI1NiJ9.SAY4ZQkXGi2cY_qz57Ah9_zDq4-bnF-oDJKotXa-LCY
|
||||
```
|
||||
|
||||
If you were to decode the access token data, it might look something like this:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="header" label="Header" default>
|
||||
|
||||
```json
|
||||
{
|
||||
"alg": "HS256",
|
||||
"typ": "JWT"
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="payload" label="Payload">
|
||||
|
||||
```json
|
||||
{
|
||||
"tenant_id": "58eb06a4-dcd7-4e96-8fac-cca2afc03e61",
|
||||
"internal": true,
|
||||
"pod": "cook",
|
||||
"org": "example",
|
||||
"identity_id": "ff80818155fe8c080155fe8d925b0316",
|
||||
"user_name": "slpt.services",
|
||||
"strong_auth": true,
|
||||
"authorities": [
|
||||
"ORG_ADMIN"
|
||||
],
|
||||
"client_id": "nKBPOwjJH8LXSjIl",
|
||||
"strong_auth_supported": true,
|
||||
"user_id": "595826",
|
||||
"scope": [
|
||||
"read",
|
||||
"write"
|
||||
],
|
||||
"exp": 1565888319,
|
||||
"jti": "c98d1236-4513-4c89-a0d0-0cb9f3276b56"
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="signature" label="Signature">
|
||||
|
||||
```text
|
||||
HMACSHA256(
|
||||
base64UrlEncode(header) + "." +
|
||||
base64UrlEncode(payload),
|
||||
{secret}
|
||||
)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You can check the JWT access token data online at [jwt.io](https://jwt.io).
|
||||
|
||||
## Authentication Details
|
||||
|
||||
This section details how to call the SailPoint Platform OAuth 2.0 token endpoints to get an `access_token`.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before any OAuth 2.0 token requests can be initiated, a Client ID and secret are necessary. As an `ORG_ADMIN`, browse to your API Management Admin Page at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` and create an API client with the appropriate grant types for your use case. If you are not an admin of your org, you can ask an admin to create this for you. Be sure to save your `Client Secret` somewhere secure, as you will not be able to view or change it later.
|
||||
|
||||
### OAuth 2.0 Token Request
|
||||
|
||||
When authenticating to IdentityNow, the OAuth 2.0 token endpoint resides on the IdentityNow API Gateway at:
|
||||
|
||||
```text
|
||||
POST https://{tenant}.api.identitynow.com/oauth/token
|
||||
```
|
||||
|
||||
How you call this endpoint to get your token depends largely on the OAuth 2.0 flow and grant type you wish to implement. The details for each grant type within IdentityNow are described in the following sections.
|
||||
|
||||
### Authorization Code Grant Flow
|
||||
|
||||
Further Reading: [https://oauth.net/2/grant-types/authorization-code/](https://oauth.net/2/grant-types/authorization-code/)
|
||||
|
||||
This grant type is used by clients to exchange an authorization code for an `access_token`. This is mainly used for web apps as there is a login into IdentityNow, with a subsequent redirect back to the web app / client.
|
||||
|
||||
The OAuth 2.0 client you are using must have `AUTHORIZATION_CODE` as one of its grant types. The redirect URLs must also match the list in the client as well:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"created": "2019-05-23T02:06:20.685Z",
|
||||
"name": "My Application",
|
||||
"description": "My Application",
|
||||
"id": "b61429f5-203d-494c-94c3-04f54e17bc5c",
|
||||
"secret": null,
|
||||
"grantTypes": [
|
||||
"AUTHORIZATION_CODE"
|
||||
],
|
||||
"redirectUris": [
|
||||
"http://localhost:8080/myApp/code"
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
<br></br>
|
||||
|
||||
### Authorization Flow
|
||||
|
||||

|
||||
|
||||
1. The user clicks the login link on a web app.
|
||||
|
||||
2. The web app sends an authorization request to IdentityNow in the form:
|
||||
|
||||
```Text
|
||||
GET https://{tenant}.identitynow.com/oauth/authorize?client_id={client-id}&client_secret={client-secret}&response_type=code&redirect_uri={redirect-url}
|
||||
```
|
||||
|
||||
3. IdentityNow redirects the user to a login prompt to authenticate to IdentityNow.
|
||||
|
||||
4. The user authenticates to IdentityNow.
|
||||
|
||||
5. Once authentication is successful, IdentityNow issues an authorization code back to the web app.
|
||||
|
||||
6. The web app submits an **OAuth 2.0 Token Request** to IdentityNow in the form:
|
||||
|
||||
```text
|
||||
POST https://{tenant}.api.identitynow.com/oauth/token?grant_type=authorization_code&client_id={client-id}&client_secret={client-secret}&code={code}&redirect_uri={redirect-url}
|
||||
```
|
||||
|
||||
>**Note**: the token endpoint URL is `{tenant}.api.identitynow.com`, while the authorize URL is `{tenant}.identitynow.com`. Be sure to use the correct URL when setting up your webapp to use this flow.
|
||||
|
||||
7. IdentityNow validates the token request and submits a response. If successful, the response will contain a JWT `access_token`.
|
||||
|
||||
The query parameters in the OAuth 2.0 token request for the Authorization Code grant are as follows:
|
||||
|
||||
| Key | Description |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| `grant_type` | Set to `authorization_code` for the authorization code grant type. |
|
||||
| `client_id` | This is the client ID for the API client (e.g. `b61429f5-203d-494c-94c3-04f54e17bc5c`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` |
|
||||
| `client_secret `| This is the client secret for the API client (e.g. `c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` |
|
||||
| `code` | This is a code returned by `/oauth/authorize`. |
|
||||
| `redirect_uri` | This is a URL of the application to redirect to once the token has been granted. |
|
||||
|
||||
Here is an example OAuth 2.0 token request for the Authorization Code grant type.
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
'https://example.api.identitynow.com/oauth/token?grant_type=authorization_code&client_id=b61429f5-203d-494c-94c3-04f54e17bc5c&client_secret=c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5&code=6688LQJB0y652z6ZjFmkCKuBUjv2sTIqKS2JthWrZ7qlPgI9TClJ6FnpweEhO6w7&redirect_uri=https://myappdomain.com/oauth/redirect' \
|
||||
-H 'cache-control: no-cache'
|
||||
```
|
||||
|
||||
### Client Credentials Grant Flow
|
||||
|
||||
Further Reading: [https://oauth.net/2/grant-types/client-credentials/](https://oauth.net/2/grant-types/client-credentials/)
|
||||
|
||||
This grant type is used by clients to obtain an access token outside the context of a user. This is probably the simplest authentication flow, but comes with a major drawback; API endpoints that require [user level permissions](https://documentation.sailpoint.com/saas/help/common/users/user_level_matrix.html) will not work. [Personal Access Tokens](#personal-access-tokens) are a form of Client Credentials that have a user context, so they do not share this drawback. However, the APIs that can be invoked with a personal access token depend on the permissions of the user that generated it.
|
||||
|
||||
An OAuth 2.0 client using the Client Credentials flow must have `CLIENT_CREDENTIALS` as one of its grantTypes:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"created": "2019-05-23T02:06:20.685Z",
|
||||
"name": "My Application",
|
||||
"description": "My Application",
|
||||
"id": "b61429f5-203d-494c-94c3-04f54e17bc5c",
|
||||
"secret": null,
|
||||
"grantTypes": [
|
||||
"CLIENT_CREDENTIALS"
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
[Personal Access Tokens](#personal-access-tokens) are implicly granted a `CLIENT_CREDENTIALS` grant type.
|
||||
|
||||
The overall authorization flow looks like this:
|
||||
|
||||
1. The client submits an **OAuth 2.0 Token Request** to IdentityNow in the form:
|
||||
|
||||
```text
|
||||
POST https://{tenant}.api.identitynow.com/oauth/token?grant_type=client_credentials&client_id={client-id}&client_secret={client-secret}
|
||||
```
|
||||
|
||||
2. IdentityNow validates the token request and submits a response. If successful, the response will contain a JWT access token.
|
||||
|
||||
The query parameters in the OAuth 2.0 Token Request for the Client Credentials grant are as follows:
|
||||
|
||||
| Key | Description |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| `grant_type` | Set to `CLIENT_CREDENTIALS` for the authorization code grant type. |
|
||||
| `client_id` | This is the client ID describing for the API client (e.g. `b61429f5-203d-494c-94c3-04f54e17bc5c`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` or by [creating a personal access token](#personal-access-tokens). |
|
||||
| `client_secret` | This is the client secret describing for the API client (e.g. `c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel` or by [creating a personal access token](#personal-access-tokens). |
|
||||
|
||||
Here is an example request to generate an `access_token` using Client Credentials.
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
'https://{tenant}.api.identitynow.com/oauth/token?grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}' \
|
||||
-H 'cache-control: no-cache'
|
||||
```
|
||||
|
||||
### Refresh Token Grant Flow
|
||||
|
||||
Further Reading: [https://oauth.net/2/grant-types/refresh-token/](https://oauth.net/2/grant-types/refresh-token/)
|
||||
|
||||
This grant type is used by clients in order to exchange a refresh token for a new `access_token` once the existing `access_token` has expired. This allows clients to continue to have a valid `access_token` without the need for the user to login as frequently.
|
||||
|
||||
The OAuth 2.0 client you are using must have `REFRESH_TOKEN` as one of its grant types, and is typically used in conjunction with another grant type, like `CLIENT_CREDENTIALS` or `AUTHORIZATION_CODE`:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"created": "2019-05-23T02:06:20.685Z",
|
||||
"name": "My Application",
|
||||
"description": "My Application",
|
||||
"id": "b61429f5-203d-494c-94c3-04f54e17bc5c",
|
||||
"secret": null,
|
||||
"grantTypes": [
|
||||
"REFRESH_TOKEN",
|
||||
"AUTHORIZATION_CODE"
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The overall authorization flow looks like this:
|
||||
|
||||
1. The client application receives an `access_token` and a `refresh_token` via one of the other OAuth grant flows, like `AUTHORIZATION_CODE`.
|
||||
2. The client application notices that the `access_token` is about to expire, based on the `expires_in` attribute contained within the JWT token.
|
||||
3. The client submits an **OAuth 2.0 Token Request** to IdentityNow in the form:
|
||||
|
||||
```text
|
||||
POST https://{tenant}.api.identitynow.com/oauth/token?grant_type=refresh_token&client_id={client_id}&client_secret={client_secret}&refresh_token={refresh_token}
|
||||
```
|
||||
|
||||
4. IdentityNow validates the token request and submits a response. If successful, the response will contain a new `access_token` and `refresh_token`.
|
||||
|
||||
The query parameters in the OAuth 2.0 Token Request for the Refresh Token grant are as follows:
|
||||
|
||||
| Key | Description |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| `grant_type` | Set to `refresh_token` for the authorization code grant type. |
|
||||
| `client_id` | This is the client ID for the API client (e.g. `b61429f5-203d-494c-94c3-04f54e17bc5c`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel`. |
|
||||
| `client_secret` | This is the client secret for the API client (e.g. `c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5`). This can be generated at `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel`. |
|
||||
| `refresh_token` | This is the `refresh_token` that was provided along with the now expired `access_token`. |
|
||||
|
||||
Here is an example call OAuth 2.0 Token Request for the Refresh Token grant.
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
'https://example.api.identitynow.com/oauth/token?grant_type=refresh_token&client_id=b61429f5-203d-494c-94c3-04f54e17bc5c&client_secret=c924417c85b19eda40e171935503d8e9747ca60ddb9b48ba4c6bb5a7145fb6c5&refresh_token=ey...4M' \
|
||||
-H 'cache-control: no-cache'
|
||||
```
|
||||
|
||||
## OAuth 2.0 Token Response
|
||||
|
||||
A successful request to `https://{tenant}.api.identitynow.com/oauth/token` will contain a response body similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRfaWQiOiI1OGViMDZhNC1kY2Q3LTRlOTYtOGZhYy1jY2EyYWZjMDNlNjEiLCJpbnRlcm5hbCI6ZmFsc2UsInBvZCI6ImNvb2siLCJvcmciOiJuZWlsLXRlc3QiLCJpZGVudGl0eV9pZCI6ImZmODA4MTgxNTVmZThjMDgwMTU1ZmU4ZDkyNWIwMzE2IiwidXNlcl9uYW1lIjoic2xwdC5zZXJ2aWNlcyIsInN0cm9uZ19hdXRoIjp0cnVlLCJhdXRob3JpdGllcyI6WyJPUkdfQURNSU4iXSwiZW5hYmxlZCI6dHJ1ZSwiY2xpZW50X2lkIjoiZmNjMGRkYmItMTA1Yy00Y2Q3LWI5NWUtMDI3NmNiZTQ1YjkwIiwiYWNjZXNzVHlwZSI6Ik9GRkxJTkUiLCJzdHJvbmdfYXV0aF9zdXBwb3J0ZWQiOmZhbHNlLCJ1c2VyX2lkIjoiNTk1ODI2Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTU2NTg5MTA2MywianRpIjoiOTQ5OWIyOTktOTVmYS00N2ZiLTgxNWMtODVkNWY2YjQzZTg2In0.zJYfjIladuGHoLXr92EOJ3A9qGNkiG5UJ9eqrtSYXAQ",
|
||||
"token_type": "bearer",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRfaWQiOiI1OGViMDZhNC1kY2Q3LTRlOTYtOGZhYy1jY2EyYWZjMDNlNjEiLCJpbnRlcm5hbCI6ZmFsc2UsInBvZCI6ImNvb2siLCJvcmciOiJuZWlsLXRlc3QiLCJpZGVudGl0eV9pZCI6ImZmODA4MTgxNTVmZThjMDgwMTU1ZmU4ZDkyNWIwMzE2IiwidXNlcl9uYW1lIjoic2xwdC5zZXJ2aWNlcyIsInN0cm9uZ19hdXRoIjp0cnVlLCJhdXRob3JpdGllcyI6WyJPUkdfQURNSU4iXSwiZW5hYmxlZCI6dHJ1ZSwiY2xpZW50X2lkIjoiZmNjMGRkYmItMTA1Yy00Y2Q3LWI5NWUtMDI3NmNiZTQ1YjkwIiwiWYNjZXNzVHlwZSI6Ik9GRkxJTkUiLCJzdHJvbmdfYXV0aF9zdXBwb3J0ZWQiOmZhbHNlLCJ1c2VyX2lkIjoiNTk1ODI2Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6Ijk0OTliMjk5LTk1ZmEtNDdmYi04MTVjLTg1ZDVmNmI0M2U4NiIsImV4cCI6MTU2NTk3NjcxMywianRpIjoiODliODk1ZDMtNTdlNC00ZDAwLWI5ZjctOTFlYWVjNDcxMGQ3In0.pfDcB0sGChdHk-oDNmiIxsKFLxq9CcPQV5-eXWgIcp4",
|
||||
"expires_in": 749,
|
||||
"scope": "read write",
|
||||
"accessType": "OFFLINE",
|
||||
"tenant_id": "58eb06a4-dcd7-4e96-8fac-cca2afc03e61",
|
||||
"internal": false,
|
||||
"pod": "cook",
|
||||
"strong_auth_supported": false,
|
||||
"org": "example",
|
||||
"user_id": "595826",
|
||||
"identity_id": "ff80818155fe8c080155fe8d925b0316",
|
||||
"strong_auth": true,
|
||||
"enabled": true,
|
||||
"jti": "9499b299-95fa-47fb-815c-85d5f6b43e86"
|
||||
}
|
||||
```
|
||||
|
||||
The `access_token` contains the JSON Web Token which is subsequently used in any further REST API calls through the IdentityNow API gateway. To use the `access_token`, simply include it in the `Authorization` header as a `Bearer` token. For example:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
'https://{tenant}.api.identitynow.com/v3/account-activities' \
|
||||
-H 'Authorization: Bearer {access_token}' \
|
||||
-H 'cache-control: no-cache'
|
||||
```
|
||||
|
||||
The `expires_in` describes the lifetime, in seconds, of the `access_token`. For example, the value 749 means that the `access_token` will expire in 12.5 minutes from the time the response was generated. The exact expiration date is also contained within the `access_token`. You can view this expiration time by decoding the JWT `access_token` using a tool like [jwt.io](https://jwt.io/).
|
||||
|
||||
The `refresh_token` contains a JSON Web Token for use in a [Refresh Token](#refresh-token-grant-flow) grant flow. The `refresh_token` will only be present if the API client has the `REFRESH_CODE` grant flow.
|
||||
|
||||
The `user_id` and `identity_id` define the identity context of the person that authenticated. This is not set for the Client Credentials grant type since it doesn't have a user context.
|
||||
|
||||
## Which OAuth 2.0 Grant Flow should I use?
|
||||
|
||||
Deciding which OAuth 2.0 grant flow you should use largely depends on your use case.
|
||||
|
||||
### Daily Work or Quick Actions
|
||||
|
||||
For daily work or short, quick administrative actions, you may not really need to worry about grant types, as an access token can easily be obtained in the user interface. In order to see this:
|
||||
|
||||
1. Login to IdentityNow.
|
||||
2. Go to `https://{tenant}.identitynow.com/ui/session`.
|
||||
3. The `accessToken` is visible in the user interface.
|
||||
4. Use this access token in the `Authorization` header when making API calls. If the access token expires, log back into Identity Now and retrieve the new access token.
|
||||
|
||||
While this is very simple to use, this is only valid for a short period of time (a few minutes).
|
||||
|
||||
### Postman
|
||||
|
||||
If you are using the popular HTTP client, [Postman](https://www.getpostman.com), you have a couple of options on how you might setup your authorization. You can just leverage the accessToken as mentioned above, or you can also configure Postman to use OAuth 2.0 directly.
|
||||
|
||||
### Web Applications
|
||||
|
||||
If you are making a web application, the best grant flow to use is the [Authorization Code](#authorization-code-grant-flow) grant flow. This will allow users to be directed to IdentityNow to login, and then redirected back to the web application via a URL redirect. This also works well with SSO, strong authentication, or pass-through authentication mechanisms.
|
||||
|
||||
SailPoint does not recommend using a password grant flow for web applications as it would involve entering IdentityNow credentials in the web application. This flow also doesn't allow you to work with SSO, strong authentication, or pass-through authentication.
|
||||
|
||||
### Scripts or Programs
|
||||
|
||||
If you are writing scripts or programs that leverage the IdentityNow APIs, which OAuth 2.0 grant from you should use typically depends on what you are doing, and which user context you need to operate under.
|
||||
|
||||
Because scripts, code, or programs do not have an interactive web-interface it is difficult, but not impossible, to implement a working [Authorization Code](#authorization-code-grant-flow) flow. Most scripts or programs typically run as a [Client Credentials](#client-credentials-grant-flow). If your APIs can work under an API context without a user, then [Client Credentials](#client-credentials-grant-flow) is ideal. However, if your APIs need a user or admin context, then the [Personal Access Token](#personal-access-tokens) approach will be more suitable.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Having issues? Follow these steps.
|
||||
|
||||
1. **Verify the API End Point Calls**
|
||||
|
||||
1. Verify the structure of the API call:
|
||||
1. Verify that the API calls are going through the API gateway:
|
||||
`https://{tenant}.api.identitynow.com`
|
||||
2. Verify you are calling their version correctly:
|
||||
- Private APIs: `https://{tenant}.api.identitynow.com/cc/api/{endpoint}`
|
||||
- V2 APIs: `https://{tenant}.api.identitynow.com/v2/{endpoint}`
|
||||
- V3 APIs: `https://{tenant}.api.identitynow.com/v3/{endpoint}`
|
||||
- Beta APIs: `https://{tenant}.api.identitynow.com/beta/{endpoint}`
|
||||
3. Verify that the API calls have the correct headers (e.g., `content-type`), query parameters, and body data.
|
||||
2. If the HTTP response is **401 Unauthorized** , this is an indication that either there is no `Authorization` header or the `access_token` is invalid. Verify that the API calls are supplying the `access_token` in the `Authorization` header correctly (ex. `Authorization: Bearer {access_token}`) and that the `access_token` has not expired.
|
||||
3. If the HTTP response is **403 Forbidden**, this is an indication that the `access_token` is valid, but the user you are running as doesn't have access to this endpoint. Check the access rights which are associated with the user.
|
||||
>**Note**: This can also be due to calling an API which expects a user, but your authorization grant type might not have a user context. Calling most administrative APIs with a CLIENT_CREDENTIAL grant will often produce this result.
|
||||
|
||||
2. **Verify the OAuth 2.0 Client**
|
||||
|
||||
1. Verify that the OAuth 2.0 Client is not a Legacy OAuth client. Legacy OAuth clients will not work.
|
||||
This is very apparent by looking at the Client ID, as OAuth 2.0 Client IDs have dashes. Here is an example:
|
||||
Legacy Client ID: `G6xLlBBOKIcOAQuK`
|
||||
OAuth 2.0 Client ID: `b61429f5-203d-494c-94c3-04f54e17bc5c`
|
||||
|
||||
2. Verify the OAuth 2.0 Client ID exists. This can be verified by calling:
|
||||
|
||||
```text
|
||||
GET /beta/oauth-clients/{client-id}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```text
|
||||
GET /beta/oauth-clients/
|
||||
```
|
||||
|
||||
You can also view all of the active clients in the UI by going to `https://{tenant}.identitynow.com/ui/admin/#admin:global:security:apimanagementpanel`.
|
||||
|
||||
3. Verify that the OAuth 2.0 Client grant types match the OAuth 2.0 grant type flow you are trying to use. For instance, this client will work with [Authorization Code](#authorization-code-grant-flow) and [Client Credentials](#client-Credentials-grant-flow) flows, but not [Refresh Token](#refresh-token-grant-flow) flows:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"created": "2019-05-23T02:06:20.685Z",
|
||||
"name": "My Application",
|
||||
"description": "My Application",
|
||||
"id": "b61429f5-203d-494c-94c3-04f54e17bc5c",
|
||||
"secret": null,
|
||||
"grantTypes": [
|
||||
"AUTHORIZATION_CODE",
|
||||
"CLIENT_CREDENTIALS"
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
4. If using an A[Authorization Code](#authorization-code-grant-flow) flow, verify the redirect URL(s) for your application match the `redirectUris` value in the client. You can check this using the [oauth-clients endpoint](/idn/api/beta/list-oauth-client).
|
||||
|
||||
3. **Verify the OAuth 2.0 Calls**
|
||||
|
||||
1. Verify that the OAuth call flow is going to the right URLs, with the correct query parameters and data values. A common source of errors is using the wrong host for authorization and token API calls. The token endpoint URL is `{tenant}.api.identitynow.com`, while the authorize URL is `{tenant}.identitynow.com`.
|
||||
@@ -6,37 +6,41 @@ sidebar_position: 1
|
||||
|
||||
# Getting Started with SailPoint APIs
|
||||
|
||||
## Finding your Org/Tenant Name
|
||||
## Find your Tenant Name
|
||||
|
||||
You will need to know your org/tenant name in order to form the proper URL for an API request. You can find your org/tenant name by logging into IdentityNow, navigating to the Admin UI, and clicking on the Dashboard dropdown and selecting the Overview page. The org name is displayed within the Org Details section of the dashboard. If you do not have admin access, you can find your tenant name, as well as the API base URL that you will use for API calls, by viewing your session details when logged into your IdentityNow instance. Simply change your URL to the following: `https://{your-IdentityNow-hostname}.com/ui/session`, where `{your-IdentityNow-hostname}` is your company's domain name for accessing IdentityNow. The session detail you want is the `baseUrl`, which will have the form of `https://{tenant}.api.identitynow.com`.
|
||||
You will need to know your org/tenant name in order to form the proper URL for an API request. You can find your org/tenant name by logging into IdentityNow, navigating to the Admin UI, and clicking on the Dashboard dropdown and selecting the Overview page. The org name is displayed within the Org Details section of the dashboard. If you do not have admin access, you can find your tenant name, as well as the API base URL that you will use for API calls, by viewing your session details when logged into your IdentityNow instance. Simply change your URL to the following: `https://{your-IdentityNow-hostname}.com/ui/session`, where `{your-IdentityNow-hostname}` is your company's domain name for accessing IdentityNow. The session detail you want is the `baseUrl`, which will have the form of `https://{tenant}.api.identitynow.com`.
|
||||
|
||||
## Making Your First API Call
|
||||
## Make Your First API Call
|
||||
|
||||
To get started, you will need create a [personal access token](./authentication.md#personal-access-tokens), which can then be used to generate access tokens to authenticate your API calls. To generate a personal access token from the IdentityNow UI, perform the following steps after logging into your IdentityNow instance:
|
||||
To get started, you will need create a [personal access token](./authentication.md#personal-access-tokens), which can then be used to generate access tokens to authenticate your API calls. To generate a personal access token from the IdentityNow UI, perform the following steps after logging into your IdentityNow instance:
|
||||
|
||||
1. Select **Preferences** from the drop-down menu under your username, then **Personal Access Tokens** on the left. You can also go straight to the page using this URL, replacing `{tenant}` with your IdentityNow tenant: `https://{tenant}.identitynow.com/ui/d/user-preferences/personal-access-tokens`.
|
||||
1. Select **Preferences** from the drop-down menu under your username, then **Personal Access Tokens** on the left. You can also go straight to the page using this URL, replacing `{tenant}` with your IdentityNow tenant: `https://{tenant}.identitynow.com/ui/d/user-preferences/personal-access-tokens`.
|
||||
|
||||
2. Click **New Token** and enter a meaningful description to help differentiate the token from others.
|
||||
|
||||
>**Note**: The **New Token** button will be disabled when you’ve reached the limit of 10 personal access tokens per user. To avoid reaching this limit, we recommend you delete any tokens that are no longer needed.
|
||||
:::caution
|
||||
The **New Token** button will be disabled when you’ve reached the limit of 10 personal access tokens per user. To avoid reaching this limit, we recommend you delete any tokens that are no longer needed.
|
||||
:::
|
||||
|
||||
3. Click **Create Token** to generate and view the two components that comprise the token: the `Secret` and the `Client ID`.
|
||||
|
||||
>**IMPORTANT**: After you create the token, the value of the `Client ID` will be visible in the Personal Access Tokens list, but the corresponding `Secret` will not be visible after you close the window. You will need to store the `Secret` somewhere secure.
|
||||
:::danger Important
|
||||
After you create the token, the value of the `Client ID` will be visible in the Personal Access Tokens list, but the corresponding `Secret` will not be visible after you close the window. You will need to store the `Secret` somewhere secure.
|
||||
:::
|
||||
|
||||
4. Copy both values somewhere that will be secure and accessible to you when you need to use the the token.
|
||||
|
||||
5. To create an `access_token` that can be used to authenticate API requests, use the following cURL command, replacing `{tenant}` with your IdentityNow tenant. The response body will contain an `access_token`, which will look like a long string of random characters.
|
||||
5. To create an `access_token` that can be used to authenticate API requests, use the following cURL command, replacing `{tenant}` with your IdentityNow tenant. The response body will contain an `access_token`, which will look like a long string of random characters.
|
||||
|
||||
```sh
|
||||
curl --location --request POST 'https://{tenant}.api.identitynow.com/oauth/token?grant_type=client_credentials&client_id={client_id}&client_secret={secret}'
|
||||
```
|
||||
```bash
|
||||
curl --location --request POST 'https://{tenant}.api.identitynow.com/oauth/token?grant_type=client_credentials&client_id={client_id}&client_secret={secret}'
|
||||
```
|
||||
|
||||
6. To test your `access_token`, execute the following cURL command, replacing `{tenant}` with your IdentityNow tenant and `access_token` with the token you generated in the previous step. If successful, you should get a JSON representation of an identity in your tenant.
|
||||
|
||||
```sh
|
||||
curl --request GET --url 'https://{tenant}.api.identitynow.com/v3/public-identities?limit=1' --header 'authorization: Bearer {access_token}'
|
||||
```
|
||||
```bash
|
||||
curl --request GET --url 'https://{tenant}.api.identitynow.com/v3/public-identities?limit=1' --header 'authorization: Bearer {access_token}'
|
||||
```
|
||||
|
||||
For more information about SailPoint Platform authentication, see [API Authentication](./authentication.md)
|
||||
|
||||
@@ -52,26 +56,26 @@ There is a rate limit of 100 requests per `access_token` per 10 seconds for V3 A
|
||||
|
||||
## Authorization
|
||||
|
||||
Each API resource requires a specific level of authorization attached to your `access_token`. These level's of authorization can be viewed in the [user level access matrix](https://documentation.sailpoint.com/saas/help/common/users/user_level_matrix.html). Please review the authorization constraints for each API endpoint to understand the user level needed to invoke the endpoint. Tokens generated outside of a user context, like the [Client Credentials](./authentication.md#client-credentials-grant-flow) grant type, will be limited in the endpoints that it can call. If your token doesn't have permission to call an endpoint, you will receive the following response:
|
||||
Each API resource requires a specific level of authorization attached to your `access_token`. These level's of authorization can be viewed in the [user level access matrix](https://documentation.sailpoint.com/saas/help/common/users/user_level_matrix.html). Please review the authorization constraints for each API endpoint to understand the user level needed to invoke the endpoint. Tokens generated outside of a user context, like the [Client Credentials](./authentication.md#client-credentials-grant-flow) grant type, will be limited in the endpoints that it can call. If your token doesn't have permission to call an endpoint, you will receive the following response:
|
||||
|
||||
**HTTP Status Code**: 403 Forbidden
|
||||
|
||||
**Response Body**:
|
||||
|
||||
```JSON
|
||||
```json
|
||||
{
|
||||
"detailCode": "403 Forbidden",
|
||||
"trackingId": "fca9eb2227514d6d90cd4a1d1cdc255c",
|
||||
"messages": [
|
||||
{
|
||||
"locale": "en-US",
|
||||
"localeOrigin": "DEFAULT",
|
||||
"text": "The server understood the request but refuses to authorize it."
|
||||
}
|
||||
]
|
||||
"detailCode": "403 Forbidden",
|
||||
"trackingId": "fca9eb2227514d6d90cd4a1d1cdc255c",
|
||||
"messages": [
|
||||
{
|
||||
"locale": "en-US",
|
||||
"localeOrigin": "DEFAULT",
|
||||
"text": "The server understood the request but refuses to authorize it."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Using an API Tool
|
||||
## API Tools
|
||||
|
||||
There are several API tools that make exploring and testing APIs easier than using the command line or a programming language. One such tool is [Postman](https://www.postman.com/downloads/). To import the SailPoint REST APIs into a tool like Postman, you must first download the REST specification. Navigate to the [V3 IDN APIs](/idn/api/v3) and click the "Download OpenAPI specification" button. You can then import the JSON file in Postman by using the [import wizard](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/) within Postman.
|
||||
There are several API tools that make exploring and testing APIs easier than using the command line or a programming language. One such tool is [Postman](https://www.postman.com/downloads/). To import the SailPoint REST APIs into a tool like Postman, you must first download the REST specification. Navigate to the [V3 IDN APIs](/idn/api/v3) and click the "Download OpenAPI specification" button. You can then import the JSON file in Postman by using the [import wizard](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/) within Postman.
|
||||
|
||||
@@ -3,14 +3,12 @@ id: standard-collection-parameters
|
||||
---
|
||||
# Standard Collection Parameters
|
||||
|
||||
Many collection endpoints in the IdentityNow API support a generic syntax for paginating, filtering
|
||||
and sorting the results.
|
||||
|
||||
A collection endpoint has the following characteristics:
|
||||
Many endpoints in the IdentityNow API support a generic syntax for paginating, filtering
|
||||
and sorting the results. A collection endpoint has the following characteristics:
|
||||
|
||||
* The HTTP verb is always GET.
|
||||
* The last component in the URL is a plural noun (ex. `/v3/public-identities`).
|
||||
* The return value from a successful request is always an array of JSON objects. This array may be empty if there are no results.
|
||||
* The return value from a successful request is always an array of JSON objects. This array may be empty if there are no results.
|
||||
|
||||
## Paginating Results
|
||||
|
||||
@@ -18,9 +16,9 @@ Pagination is achieved with the following optional query parameters.
|
||||
|
||||
|Name|Description|Default|Constraints|
|
||||
|---|---|---|---|
|
||||
|**limit**|Integer that specifies the maximum number of records to return in a single API call. If not specified a default limit will be used.|250|Maxiumum of 250 records per page|
|
||||
|**offset**|Integer that specifies the offset of the first result from the beginning of the collection. **offset** is record based, not page based, and the index starts at 0. For example, **offset=0** and **limit=20** will return records 0-19, while **offset=1** and **limit=20** will return records 1-20.|0|Between 0 and the last record index.
|
||||
|**count**|Boolean that indicates whether a total count will be returned, factoring in any filter parameters, in the **X-Total-Count** response header. The value will be the total size of the collection that would be returned if **limit** and **offset** were ignored. For example, if the total number of records is 1000, then count=true would return 1000 in the **X-Total-Count** header. Since requesting a total count can have performance impact, it is recommended not to send **count=true** if no use is being made of that value.|false|Must be **true** or **false**|
|
||||
|`limit`|Integer that specifies the maximum number of records to return in a single API call. If not specified a default limit will be used.|250|Maxiumum of 250 records per page|
|
||||
|`offset`|Integer that specifies the offset of the first result from the beginning of the collection. **offset** is record based, not page based, and the index starts at 0. For example, **offset=0** and **limit=20** will return records 0-19, while **offset=1** and **limit=20** will return records 1-20.|0|Between 0 and the last record index.
|
||||
|`count`|Boolean that indicates whether a total count will be returned, factoring in any filter parameters, in the **X-Total-Count** response header. The value will be the total size of the collection that would be returned if **limit** and **offset** were ignored. For example, if the total number of records is 1000, then count=true would return 1000 in the **X-Total-Count** header. Since requesting a total count can have performance impact, it is recommended not to send **count=true** if no use is being made of that value.|false|Must be **true** or **false**|
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -30,7 +28,7 @@ Examples:
|
||||
|
||||
## Filtering Results
|
||||
|
||||
Any collection with a `filters` parameter supports filtering. This means that an item will only be included in the returned array if the filters expression evaluates to true for that item. Check the available request parameters for the collection endpoint you are using to see if it supports filtering.
|
||||
Any collection with a `filters` parameter supports filtering. This means that an item will only be included in the returned array if the filters expression evaluates to true for that item. Check the available request parameters for the collection endpoint you are using to see if it supports filtering.
|
||||
|
||||
### Data Types
|
||||
|
||||
@@ -43,7 +41,7 @@ Filter expressions are applicable to fields of the following types:
|
||||
|
||||
### Filter Syntax
|
||||
|
||||
The syntax of V3 filters is similar to, but not exactly the same as, that specified by the SCIM standard. Some key differences are
|
||||
The syntax of V3 filters is similar to, but not exactly the same as, that specified by the SCIM standard. Some key differences are:
|
||||
|
||||
* A slightly different set of supported operators
|
||||
* Case-sensitivity of operators. All V3 filter operators are in lowercase; it is illegal to specify "EQ" instead of "eq".
|
||||
@@ -54,17 +52,17 @@ These filter operators apply directly to fields and their values:
|
||||
|
||||
|Operator|Description|Example|
|
||||
|---|---|---|
|
||||
|ca|True if the collection-valued field contains all the listed values.|groups ca ("Venezia","Firenze")|
|
||||
|co|True if the value of the fieldcontains the specified value as a substring.(Applicable to string-valued fields only.)|name co "Rajesh"|
|
||||
|eq|True if the value of the field indicated by the first operand isequal to the value specified by the second operand.|identitySummary.id eq "2c9180846e85e4b8016eafeba20c1314"|
|
||||
|ge|True if the value of the field indicated by the first operand isgreater or equal to the value specified by the second operand.|daysUntilEscalation ge 7 name ge "Genaro"|
|
||||
|gt|True if the value of the field indicated by the first operand isgreater than the value specified by the second operand.|daysUntilEscalation gt 7 name gt "Genaro" created gt 2018-12-18T23:05:55Z|
|
||||
|in|True if the field value is in the list of values.|accountActivityItemId in ("2c9180846b0a0583016b299f210c1314","2c9180846b0a0581016b299e82560c1314")|
|
||||
|le|True if the value of the field indicated by the first operand is less or equal to the value specified by the second operand.|daysUntilEscalation le 7 name le "Genaro"|
|
||||
|lt|True if the value of the field indicated by the first operand is less than the value specified by the second operand.|daysUntilEscalation lt 7 name lt "Genaro" created lt 2018-12-18T23:05:55Z|
|
||||
|ne|True if the value of the field indicated by the first operand isnot equal to the value specified by the second operand.|type ne "ROLE"|
|
||||
|pr|True if the field is present, that is, not null.|pr accountRequestInfo|
|
||||
|sw|True if the value of the field starts with the specified value.(Applicable to string-valued fields only.)|name sw "Rajesh"|
|
||||
|`ca`|True if the collection-valued field contains all the listed values.|groups ca ("Venezia","Firenze")|
|
||||
|`co`|True if the value of the fieldcontains the specified value as a substring.(Applicable to string-valued fields only.)|name co "Rajesh"|
|
||||
|`eq`|True if the value of the field indicated by the first operand isequal to the value specified by the second operand.|identitySummary.id eq "2c9180846e85e4b8016eafeba20c1314"|
|
||||
|`ge`|True if the value of the field indicated by the first operand isgreater or equal to the value specified by the second operand.|daysUntilEscalation ge 7 name ge "Genaro"|
|
||||
|`gt`|True if the value of the field indicated by the first operand isgreater than the value specified by the second operand.|daysUntilEscalation gt 7 name gt "Genaro" created gt 2018-12-18T23:05:55Z|
|
||||
|`in`|True if the field value is in the list of values.|accountActivityItemId in ("2c9180846b0a0583016b299f210c1314","2c9180846b0a0581016b299e82560c1314")|
|
||||
|`le`|True if the value of the field indicated by the first operand is less or equal to the value specified by the second operand.|daysUntilEscalation le 7 name le "Genaro"|
|
||||
|`lt`|True if the value of the field indicated by the first operand is less than the value specified by the second operand.|daysUntilEscalation lt 7 name lt "Genaro" created lt 2018-12-18T23:05:55Z|
|
||||
|`ne`|True if the value of the field indicated by the first operand isnot equal to the value specified by the second operand.|type ne "ROLE"|
|
||||
|`pr`|True if the field is present, that is, not null.|pr accountRequestInfo|
|
||||
|`sw`|True if the value of the field starts with the specified value.(Applicable to string-valued fields only.)|name sw "Rajesh"|
|
||||
|
||||
### Composite Operators
|
||||
|
||||
@@ -72,13 +70,13 @@ These operators are applied to other filter expressions:
|
||||
|
||||
|Operator|Description|Example|
|
||||
|---|---|---|
|
||||
|and|True if both the filter-valued operands are true.|startDate gt 2018 and name sw "Genaro"|
|
||||
|not|True if the filter-valued operand is false.|not groups ca ("Venezia","Firenze")|
|
||||
|or|True if either of the filter-valued operands are true.|startDate gt 2018 or name sw "Genaro"|
|
||||
|`and`|True if both the filter-valued operands are true.|startDate gt 2018 and name sw "Genaro"|
|
||||
|`not`|True if the filter-valued operand is false.|not groups ca ("Venezia","Firenze")|
|
||||
|`or`|True if either of the filter-valued operands are true.|startDate gt 2018 or name sw "Genaro"|
|
||||
|
||||
### Escaping Special Characters in a Filter
|
||||
|
||||
Certain characters must be escaped before they can be used in a filter expression. For example, the following filter expression that attempts to find all sources with the name `#Employees` will produce a 400 error:
|
||||
Certain characters must be escaped before they can be used in a filter expression. For example, the following filter expression that attempts to find all sources with the name `#Employees` will produce a 400 error:
|
||||
|
||||
`/v3/sources?filters=name eq "#Employees"`
|
||||
|
||||
@@ -102,32 +100,24 @@ The following table lists the special characters that are incompatible with `fil
|
||||
|
||||
### Known Limitations
|
||||
|
||||
Although filter expressions are a very general mechanism, individual API endpoints will only support filtering on a specific set of fields that are relevant to that endpoint, and will frequently only support a subset of operations for each field. For example, an endpoint might allow filtering on the name field but not support use of the co operator on that field. Consult the documentation for each API endpoint to determine what fields and operators can be used.
|
||||
Although filter expressions are a very general mechanism, individual API endpoints will only support filtering on a specific set of fields that are relevant to that endpoint, and will frequently only support a subset of operations for each field. For example, an endpoint might allow filtering on the name field but not support use of the co operator on that field. Consult the documentation for each API endpoint to determine what fields and operators can be used. Attempts to use an unsupported filter expression will result in a 400 Bad Request response.
|
||||
|
||||
Examples:
|
||||
|
||||
* `/v3/public-identities?filters=email eq "john.doe@example.com"`
|
||||
* `/v3/public-identities?filters=firstname sw "john" or email sw "joe"`
|
||||
* `not prop1 eq val1 or prop2 eq val2 and prop3 eq val3` is equivalent to `(not (prop1 eq val1)) or ((prop2 eq val2) and (prop3 eq val3))`
|
||||
* `not (prop1 eq val1 or prop2 eq val2) and prop3 eq val3` is equivalent to `(not ((prop1 eq val1) or (prop2 eq val2))) and (prop3 eq val3)`
|
||||
|
||||
Attempts to use an unsupported filter expression will result in a 400 Bad Request response.
|
||||
|
||||
**NOTES:**
|
||||
|
||||
* Spaces in URLs must be escaped with `%20`. Most programming languages, frameworks, libraries, and tools will do this for you, but some won't. In the event that your tool doesn't escape spaces, you will need to format your query as `/v3/public-identities?filters=email%20eq%20"john.doe@example.com"`
|
||||
:::info
|
||||
* Spaces in URLs must be escaped with `%20`. Most programming languages, frameworks, libraries, and tools will do this for you, but some won't. In the event that your tool doesn't escape spaces, you will need to format your query as `/v3/public-identities?filters=email%20eq%20"john.doe@example.com"`
|
||||
|
||||
* Unless explicitly noted otherwise, strings are compared lexicographically. Most comparisons are not case sensitive. Any situations where the comparisons are case sensitive will be called out.
|
||||
|
||||
* Date-times are compared temporally; an earlier date-time is less than a later date-time.
|
||||
|
||||
* The usual precedence/associativity of the composite operators applies, with **not** having higher priority than **and**, which in turn has higher priority than **or**. Parentheses can be used to override this precedence.
|
||||
|
||||
Examples:
|
||||
|
||||
`not prop1 eq val1 or prop2 eq val2 and prop3 eq val3` is equivalent to `(not (prop1 eq val1)) or ((prop2 eq val2) and (prop3 eq val3))`
|
||||
|
||||
and
|
||||
|
||||
`not (prop1 eq val1 or prop2 eq val2) and prop3 eq val3` is equivalent to `(not ((prop1 eq val1) or (prop2 eq val2))) and (prop3 eq val3)`
|
||||
:::
|
||||
|
||||
### Sorting Results
|
||||
|
||||
@@ -137,7 +127,7 @@ For example, to sort primarily by **type** in ascending order, and secondarily b
|
||||
|
||||
## Putting it all Together
|
||||
|
||||
Pagination, filters, and sorters can be mixed and match to achieve the desired output for a given collection endpoint. Here are some examples:
|
||||
Pagination, filters, and sorters can be mixed and match to achieve the desired output for a given collection endpoint. Here are some examples:
|
||||
|
||||
* `/v3/public-identities?limit=20&filters=firstname eq "john"&sorters=-name` returns the first 20 identities that have a first name of John and are sorted in descending order by full name.
|
||||
* `/v3/account-activities?limit=10&offset=2&sorters=-created` sorts the results by descending created time, so the most recent activities appear first. The limit and offset returns the 3rd page of this sorted response with 10 records displayed.
|
||||
* `/v3/account-activities?limit=10&offset=2&sorters=-created` sorts the results by descending created time, so the most recent activities appear first. The limit and offset returns the 3rd page of this sorted response with 10 records displayed.
|
||||
|
||||
Reference in New Issue
Block a user