---
title: Application grant workflow example
description: Example workflow for application access requests with context checks and line-of-business routing
component: pingoneaic
page_id: pingoneaic:identity-governance:administration/example-app-grant-workflow
canonical_url: https://docs.pingidentity.com/pingoneaic/identity-governance/administration/example-app-grant-workflow.html
llms_txt: https://docs.pingidentity.com/pingoneaic/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
keywords: ["workflows", "use cases", "examples", "application grant"]
section_ids:
  assumptions: Assumptions
  example: Example
---

# Application grant workflow example

In this example, an administrator wants to create an application grant workflow that:

* Requires the manager to approve the request. If an administrator sends a request, the request is auto-approved.

* If approved, check what line of business (LOB) the application is in.

* Based on the LOB, the workflow requires a separate approver to approve the request.

## Assumptions

* Each application has an application owner. An application owner is a user identity designated to manage the application. You populate this value for each target application.

* You create an [application glossary attribute](glossary.html#create-application-glossary) LOB, and populate the LOB for each application. For this scenario, the LOBs are:

  * `Sales`

  * `Finance`

  * `Human Resources`

* Your end users have a manager assigned to them. An administrator populates this property and it isn't modifiable by the end user.

## Example

![An example of an application grant workflow.](../_images/governance-example-app-workflow-lob.png)

* 1 Use a Script node to do a context check for the request.

  > **Collapse: Click to display the request context check script**
  >
  > ```js
  > /*
  > Script nodes are used to invoke APIs or execute business logic.
  > You can invoke governance APIs or IDM APIs.
  > You can find details in Manage workflows.
  >
  > Script nodes should return a single value and should have the
  > logic enclosed in a try-catch block.
  >
  > Example:
  > try {
  >   var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {});
  >   applicationId = requestObj.application.id;
  > }
  > catch (e) {
  >   failureReason = 'Validation failed: Error reading request with id ' + requestId;
  > }
  > */
  > var content = execution.getVariables();
  > var requestId = content.get('id');
  > var context = null;
  > var skipApproval = false;
  > var lineItemId = false;
  > try {
  >   var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {});
  >   if (requestObj.request.common.context) {
  >     context = requestObj.request.common.context.type;
  >     lineItemId = requestObj.request.common.context.lineItemId;
  >     if (context == 'admin') {
  >       skipApproval = true;
  >     }
  >   }
  > }
  > catch (e) {
  >   logger.info("Request Context Check failed "+e.message);
  > }
  >
  > logger.info("Context: " + context);
  > execution.setVariable("context", context);
  > execution.setVariable("lineItemId", lineItemId);
  > execution.setVariable("skipApproval", skipApproval);
  > ```

* 2 Use an IF/ELSE node to set the context gateway for auto approval or standard approval based on a manager review.

* 3 Use the Script node to run any auto approvals:

  > **Collapse: Click to display the auto approval script**
  >
  > ```js
  > var content = execution.getVariables();
  > var requestId = content.get('id');
  > var context = content.get('context');
  > var lineItemId = content.get('lineItemId');
  > var queryParams = {
  >   "_action": "update"
  > }
  > try {
  >   var decision = {
  >       "decision": "approved",
  >       "comment": "Request auto-approved due to request context: " + context
  >   }
  >   openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams);
  > }
  > catch (e) {
  >   var failureReason = "Failure updating decision on request. Error message: " + e.message;
  >   var update = {'comment': failureReason, 'failure': true};
  >   openidm.action('iga/governance/requests/' + requestId, 'POST', update, queryParams);
  > }
  > ```

* 4 Using an Approval node, the manager of the end user must approve the request.

* 5 If approved, a Script node checks the application glossary attribute `lineOfBusiness` (LOB) and sets the outcome based on the LOB of the application. Based on the outcome, the Switch node evaluates the LOB.

  > **Collapse: Click to display check LOB script**
  >
  > ```js
  > var content = execution.getVariables();
  > var requestId = content.get('id');
  > var requestObj = null;
  > var appId = null;
  > var appGlossary = null;
  > var lob = null;
  >
  > try {
  >   requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {});
  >   appId = requestObj.application.id;
  >   }
  >   catch (e) {
  > 	logger.info("Validation failed: Error reading application grant request with id " + requestId);
  >   }
  >
  > try {
  >   appGlossary = openidm.action('iga/governance/application/' + appId + '/glossary', 'GET', {}, {});
  >   lob = appGlossary.lineOfBusiness || "default";
  >   execution.setVariable("lob", lob);
  > }
  > catch (e) {
  >   logger.info("Could not retrieve glossary with appId " + appId + " from application grant request ID " + requestId);
  > }
  > ```

* 3 If the LOB is:

  * `sales`: An Approval node requires members of the role `Sales App Approver` to approve the request.

  * `finance`: An Approval node requires members of the role `Finance App Approver` to approve the request.

  * `humanResources`: An Approval node requires members of the role `Human Resources App Approver` to approve the request.

  * `null`: An Approval node requires the application owner to approve the request.

* 7 If the required approvals are met, a Script node runs a validation check.

  > **Collapse: Click to display app grant validation script**
  >
  > ```js
  > logger.info("Running application grant request validation");
  >
  > var content = execution.getVariables();
  > var requestId = content.get('id');
  > var failureReason = null;
  > var applicationId = null;
  > var app = null;
  >
  > try {
  >   var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {});
  >   applicationId = requestObj.application.id;
  > }
  > catch (e) {
  >   failureReason = "Validation failed: Error reading request with id " + requestId;
  > }
  >
  > // Validation 1 - Check application exists
  > if (!failureReason) {
  >   try {
  >     app = openidm.read('managed/alpha_application/' + applicationId);
  >     if (!app) {
  >       failureReason = "Validation failed: Cannot find application with id " + applicationId;
  >     }
  >   }
  >   catch (e) {
  >     failureReason = "Validation failed: Error reading application with id " + applicationId + ". Error message: " + e.message;
  >   }
  > }
  >
  > // Validation 2 - Check the user doesn't already have application granted
  > // Note: this is done at request submission time as well, the following is an example of how to check user's accounts
  > if (!failureReason) {
  >   try {
  >     var user = openidm.read('managed/alpha_user/' + requestObj.user.id, null, [ 'effectiveApplications' ]);
  >     user.effectiveApplications.forEach(effectiveApp => {
  >       if (effectiveApp._id === applicationId) {
  >         failureReason = "Validation failed: User with id " + requestObj.user.id + " already has effective application " + applicationId;
  >       }
  >     })
  >   }
  >   catch (e) {
  >     failureReason = "Validation failed: Unable to check effective applications of user with id " + requestObj.user.id + ". Error message: " + e.message;
  >   }
  > }
  >
  > if (failureReason) {
  >   logger.info("Validation failed: " + failureReason);
  > }
  > execution.setVariable("failureReason", failureReason);
  > ```

  If any Approval node has the `Reject` outcome, a Script node denies the request.

  > **Collapse: Click to display reject request script**
  >
  > ```js
  > logger.info("Rejecting request");
  >
  > var content = execution.getVariables();
  > var requestId = content.get('id');
  >
  > logger.info("Execution Content: " + content);
  > var requestIndex = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {});
  > var decision = {'outcome': 'denied', 'status': 'complete', 'decision': 'rejected'};
  > var queryParams = { '_action': 'update'};
  > openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams);
  > ```

* 8 If the If/Else node outcome is:

  * `validationSuccess`: A Script node provisions the application to the end user.

    > **Collapse: Click to display auto provisioning script**
    >
    > ```js
    > logger.info("Auto-Provisioning");
    >
    > var content = execution.getVariables();
    > var requestId = content.get('id');
    > var failureReason = null;
    >
    > try {
    >   var requestObj = openidm.action('iga/governance/requests/' + requestId, 'GET', {}, {});
    >   logger.info("requestObj: " + requestObj);
    > }
    > catch (e) {
    >   failureReason = "Provisioning failed: Error reading request with id " + requestId;
    > }
    >
    > if(!failureReason) {
    >   try {
    >     var request = requestObj.request;
    >     var payload = {
    >       "applicationId": request.common.applicationId,
    >       "startDate": request.common.startDate,
    >       "endDate": request.common.endDate,
    >       "auditContext": {},
    >       "grantType": "request"
    >     };
    >     var queryParams = {
    >       "_action": "add"
    >     }
    >
    >     logger.info("Creating account: " + payload);
    >     var result = openidm.action('iga/governance/user/' + request.common.userId + '/applications' , 'POST', payload,queryParams);
    >   }
    >   catch (e) {
    >     failureReason = "Provisioning failed: Error provisioning account to user " + request.common.userId + " for application " + request.common.applicationId + ". Error message: " + e.message;
    >   }
    >
    >   var decision = {'status': 'complete', 'decision': 'approved'};
    >   if (failureReason) {
    >     decision.outcome = 'not provisioned';
    >     decision.comment = failureReason;
    >     decision.failure = true;
    >   }
    >   else {
    >     decision.outcome = 'provisioned';
    >   }
    >
    >   var queryParams = { '_action': 'update'};
    >   openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams);
    >   logger.info("Request " + requestId + " completed.");
    > }
    > ```

  * `validationFailure`: A Script node doesn't provision the application to the end user.

    > **Collapse: Click to display validation failure script**
    >
    > ```js
    > var content = execution.getVariables();
    > var requestId = content.get('id');
    > var failureReason = content.get('failureReason');
    >
    > var decision = {'outcome': 'not provisioned', 'status': 'complete', 'comment': failureReason, 'failure': true, 'decision': 'approved'};
    > var queryParams = { '_action': 'update'};
    > openidm.action('iga/governance/requests/' + requestId, 'POST', decision, queryParams);
    > ```

|   |                                                                                                                                                                                                                                           |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Download the JSON file for this workflow [here](../_attachments/workflows/workflowUIAppGrantWorkflowExample.json).Learn more about how to import or export workflows in [workflow editor canvas](workflow-configure.html#orch-ui-canvas). |
