---
title: PolicyEnforcementFilter
description: Requests policy decisions from AM, which allows or denies the request based on the request context, the request URI, and the AM policies.
component: pinggateway
version: 7.2
page_id: pinggateway:reference:PolicyEnforcementFilter
canonical_url: https://docs.pingidentity.com/pinggateway/2026/reference/PolicyEnforcementFilter.html
llms_txt: https://docs.pingidentity.com/pinggateway/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
revdate: 2025-06-02T16:30:54Z
superseded_by: https://docs.pingidentity.com/pinggateway/2026/reference/PolicyEnforcementFilter.html
section_ids:
  PolicyEnforcementFilter-process-decision: Processing requests after a policy decision
  pef-advices: Using advices from policy decisions
  PolicyEnforcementFilter-conf-am: Notes on configuring policies in AM
  pef-eviction: WebSocket notifications for policy changes
  PolicyEnforcementFilter-usage: Usage
  PolicyEnforcementFilter-properties: Properties
  PEF-example: Example policy enforcement using claimsSubject
  PolicyEnforcementFilter-moreinfo: More information
---

# PolicyEnforcementFilter

Requests policy decisions from AM, which allows or denies the request based on the request context, the request URI, and the AM policies.

## Processing requests after a policy decision

After a policy decision, IG continues to process requests as follows:

* If the request is allowed, processing continues.

* If the request is denied with advices, IG checks whether it can respond to the advices. If IG can respond, it sends a redirect and information about how to meet the conditions in the advices.

  By default, the request is redirected to AM. If the SingleSignOnFilter property `loginEndpoint` is configured, the request is redirected to that endpoint.

* If the request is denied without advice, or if IG cannot respond to the advice, IG forwards the request to a `failureHandler` declared in the `PolicyEnforcementFilter`. If there is no `failureHandler`, IG returns a 403 Forbidden.

* If an error occurs during the process, IG returns 500 Internal Server Error.

## Using advices from policy decisions

Attributes and advices returned by the policy decision are stored in the `policyDecision` context. For information, see [PolicyDecisionContext](RequestsResponsesContexts.html#PolicyDecisionContext).

IG responds to the following advice types from AM:

* `AuthLevel`: The minimum authentication level at which a user-agent must authenticate to access a resource.

* `AuthenticateToService`: The name of an authorization chain or service to which a user-agent must authenticate to access a resource.

* `AuthenticateToRealm`: The name of a realm to which a user-agent must authenticate to access a resource.

* `AuthScheme`: The name of an authentication module to which a user-agent must authenticate to access a resource, the policy set name, and the authentication timeout.

* `Transaction`: The additional actions that a user-agent must perform before having a one-time access to the protected resource.

## Notes on configuring policies in AM

In the AM policy, remember to configure the `Resources` parameter with the URI of the protected application.

The request URI from IG must match the `Resources` parameter defined in the AM policy. If the URI of the incoming request is changed before it enters the policy filter (for example, by rebasing or scripting), remember to change the `Resources` parameter in AM policy accordingly.

## WebSocket notifications for policy changes

When WebSocket notifications are set up for changes to policies, IG receives a notification from AM when a policy decision is created, deleted, or updated.

For information about setting up WebSocket notifications, using them to clear the policy cache, and including them in the server logs, see [WebSocket Notifications](../maintenance-guide/tuning.html#amservice-websocket).

## Usage

```json
{
  "name": string,
  "type": "PolicyEnforcementFilter",
  "config": {
    "amService": AmService reference,
    "pepRealm": configuration expression<string>,
    "ssoTokenSubject": runtime expression<string>,
    "jwtSubject": runtime expression<string>,
    "claimsSubject": map,
    "cache": object,
    "application": configuration expression<string>,
    "environment": map,
    "failureHandler": Handler reference,
    "resourceUriProvider": ResourceUriProvider reference,
    "executor": Executor service reference //deprecated
  }
}
```

## Properties

* `"amService"`: *AmService [reference](preface.html#definition-reference), required*

  The AmService object to use for the following properties:

  * `agent`, the credentials of the IG agent in AM. When the agent is authenticated, the token can be used for tasks such as getting the user's profile, making policy evaluations, and connecting to the AM notification endpoint.

  * `realm`: Realm of the IG agent in AM.

  * `url`, the URL of an AM service to use for session token validation and authentication.

  * `ssoTokenHeader`, the name of the HTTP header that provides the session token created by AM

  * `amHandler`, the handler to use when requesting policy decisions from AM.

  * `version`: The version of the AM server.

    The AM version is derived as follows, in order of precedence:

    * Discovered value: AmService discovers the AM version. If `version` is configured with a different value, AmService ignores the value of `version` and issues a warning.

    * Value in `version`: AmService cannot discover the AM version, and `version` is configured.

    * Default value of AM 6: AmService cannot discover the AM version, and `version` is not configured.

    See also [AmService](MiscellaneousConfigurationObjects.html#AmService).

* `"pepRealm"`: *configuration expression<[string](preface.html#definition-string)>, optional*

  The AM realm where the policy set is located.

  Default: The realm declared for `amService`.

* `"ssoTokenSubject"`: \_runtime expression<[string](preface.html#definition-string)>, required if neither of the following properties are present: `jwtSubject`, `claimsSubject`

  The AM SSO or CDSSO token ID string for the subject making the request to the protected resource.

  `ssoTokenSubject` can take the following values:

  * `${contexts.ssoToken.value}`, when the SingleSignOnFilter is used for authentication

  * `${contexts.ssoToken.value}` or `${contexts.cdsso.token}`, when the CrossDomainSingleSignOnFilter is used for authentication

    When there is no SSO (API protection), `ssoTokenSubject` usually points to a header value such as `${request.headers.iPlanetDirectoryPro[0]}`, where `iPlanetDirectoryPro` is the name of the default AM session cookie. To find the name of your AM session cookie, see [Find the name of your AM session cookie](../gateway-guide/preface.html#am-session-cookie).

* `"jwtSubject"`: \_runtime expression<[string](preface.html#definition-string)>, required if neither of the following properties are present: `ssoTokenSubject`, `claimsSubject`

  The JWT string for the subject making the request to the protected resource.

  To use the raw id_token (base64, not decoded) returned by the OpenID Connect Provider during authentication, place an `AuthorizationCodeOAuth2ClientFilter` filter before the PEP filter, and then use `${attributes.openid.id_token}` as the expression value.

  See also [AuthorizationCodeOAuth2ClientFilter](Filters.html#AuthorizationCodeOAuth2ClientFilter) and [Expressions](Expressions.html#Expressions).

* `"claimsSubject"`: *[map](preface.html#definition-map), required if neither of the following properties are present: `jwtSubject`, \`"ssoTokenSubject\`*

  A representation of JWT claims for the subject.

  The claim `"sub"` must be specified. Other claims are optional.

  The map can be structured as follows:

  * As a runtime expression of a map, where the evaluation is an object of type `Map<String, Object>`.

  * As a map whose key is a string, and whose value is a runtime expression.

  Examples

  ```json
  "claimsSubject": "${attributes.openid.id_token_claims}"
  ```

  ```json
  "claimsSubject": {
    "sub": "${attributes.subject_identifier}",
    "iss": "am.example.com"
  }
  ```

  For an example of using `claimsSubject` as a map, see [Example Policy Enforcement Using claimsSubject](Filters.html#PEF-example) on this reference page.

* `"application"`: *configuration expression<[string](preface.html#definition-string)>, optional*

  The ID of the AM policy set to use when requesting policy decisions.

  Default: `iPlanetAMWebAgentService`, provided by AM's default policy set

* `cache`: *[object](preface.html#definition-object), optional*

  Enable and configure caching of policy decisions from AM, based on *Caffeine*. For more information, see the GitHub entry, [Caffeine](https://github.com/ben-manes/caffeine).

  ```json
  {
    "cache": {
      "enabled": configuration expression<boolean>,
      "defaultTimeout": configuration expression<duration>,
      "executor": Executor service reference,
      "maximumSize": configuration expression<number>,
      "maximumTimeToCache": configuration expression<duration>,
      "onNotificationDisconnection": configuration expression<enumeration>
    }
  }
  ```

  Default: Policy decisions are not cached.

  |   |                                                         |
  | - | ------------------------------------------------------- |
  |   | Policy decisions that contain advices are never cached. |

  The following code example caches AM policy decisions without advices for these times:

  * One hour, when the policy decision doesn't provide a `ttl` value.

  * The duration specified by the `ttl`, when `ttl` is shorter than one day.

  * One day, when `ttl` is longer than one day.

  ```json
  "cache": {
    "enabled": true,
    "defaultTimeout": "1 hour",
    "maximumTimeToCache": "1 day"
  }
  ```

  * `enabled`: *configuration expression<[boolean](preface.html#definition-boolean)>, optional*

    Enable or disable caching of policy decisions.

    When a policy decision is cached, IG can reuse the policy decision without repeatedly asking AM for a new policy decision. When caching is disabled, IG must ask AM to make a decision for each request.

    Default: `false`

  * `defaultTimeout`: *configuration expression<[duration](preface.html#definition-duration)>, optional*

    The default duration for which to cache AM policy decisions.

    If an AM policy decision provides a valid `ttl` value to specify the time until which the policy decision remains valid, IG uses that value or the `maxTimeout`.

    Default: `1 minute`

  * `"executor"`: *Executor service [reference](preface.html#definition-reference), optional*

    An executor service to schedule the execution of tasks, such as the eviction of entries in the cache.

    Default: `ForkJoinPool.commonPool()`

  * `"maximumSize"`: *configuration expression<[number](preface.html#definition-number)>, optional*

    The maximum number of entries the cache can contain.

    Default: Unlimited/unbound.

  * `maximumTimeToCache`: *configuration expression<[duration](preface.html#definition-duration)>, optional*

    The maximum duration for which to cache AM policy decisions.

    If the `ttl` value provided by the AM policy decision is after the current time plus the `maximumTimeToCache`, IG uses the `maximumTimeToCache`.

    The duration cannot be `zero` or `unlimited`.

  * `onNotificationDisconnection`: *configuration expression\<enumeration>, optional*

    The strategy to manage the cache when the WebSocket notification service is disconnected, and IG receives no notifications for AM events. If the cache is not cleared it can become outdated, and IG can allow requests on revoked sessions or tokens.

    Cached entries that expire naturally while the notification service is disconnected are removed from the cache.

    Use one of the following values:

    * `NEVER_CLEAR`

      * When the notification service is disconnected:

        * Continue to use the existing cache.

        * Deny access for requests that are not cached, but do not update the cache with these requests.

      * When the notification service is reconnected:

        * Continue to use the existing cache.

        * Query AM for incoming requests that are not found in the cache, and update the cache with these requests.

    * `CLEAR_ON_DISCONNECT`

      * When the notification service is disconnected:

        * Clear the cache.

        * Deny access to all requests, but do not update the cache with these requests.

      * When the notification service is reconnected:

        * Query AM for all requests that are not found in the cache. (Because the cache was cleared, the cache is empty after reconnection.)

        * Update the cache with these requests.

    * `CLEAR_ON_RECONNECT`

      * When the notification service is disconnected:

        * Continue to use the existing cache.

        * Deny access for requests that are not cached, but do not update the cache with these requests.

      * When the notification service is reconnected:

        * Query AM for all requests that are not found in the cache. (Because the cache was cleared, the cache is empty after reconnection.)

        * Update the cache with these requests.

    Default: `CLEAR_ON_DISCONNECT`

* `"environment"`: *[map](preface.html#definition-map), optional*

  Strings to forward to AM with the a policy decision request, to represent the environment (or context) of the request. Forward any HTTP header or any value that the AM policy definition can use.

  The map can be structured as follows:

  * As a runtime expression of a map, where the evaluation is an object of type `Map<String, Object>`.

  * As a map whose key is a string, and whose value is a runtime expression.

  AM can use the environment conditions to set the circumstances under which a policy applies. For example, environment conditions can specify that the policy applies only during working hours or only when accessing from a specific IP address.

  In the following example, the `PolicyEnforcementFilter` forwards standard and non-standard request headers, an ID token, and the IP address of the subject making the request:

  ```json
  "environment": {
    "H-Via": "${request.headers['Via']}",
    "H-X-Forwarded-For": "${request.headers['X-Forwarded-For']}",
    "H-myHeader": "${request.headers['myHeader']}",
    "id_token": [
      "${attributes.openid.id_token}"
    ],
    "IP": [
      "${contexts.client.remoteAddress}"
    ]
  }
  ```

* `"failureHandler"`: *Handler [reference](preface.html#definition-reference), optional*

  Handler to treat the request if it is denied by the policy decision.

  In the following example, the `failureHandler` is a chain with a scriptable filter. If there are some advices with the policy decision, the script recovers the advices for processing. Otherwise, it passes the request to the `StaticResponseHandler` to display a message.

  ```json
  "failureHandler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "type": "ScriptableFilter",
          "config": {
            "type": "application/x-groovy",
            "source": [
              "if (contexts.policyDecision.advices['MyCustomAdvice'] != null) {",
              "  return handleCustomAdvice(context, request)",
              "} else {",
              "  return next.handle(context, request)",
              "}"
            ]
          }
        }
      ],
      "handler": {
        "type": "StaticResponseHandler",
        "config": {
          "status": 403,
          "headers": {
            "Content-Type": [ "text/plain; charset=UTF-8" ]
          },
          "entity": "Restricted area. You do not have sufficient privileges."
        }
      }
    }
  }
  ```

  Provide an inline handler configuration object, or the name of a handler object declared in the heap. See also [Handlers](Handlers.html).

  Default: HTTP 403 Forbidden, the request stops being executed.

* `"resourceUriProvider"`: *ResourceUriProvider [reference](preface.html#definition-reference), optional*

  Return the resource URL to include in policy decision requests to AM. Configure one of the following ResourceUriProviders inline or in the heap:

  * `RequestResourceUriProvider`

  * `ScriptableResourceUriProvider`

  Default: `RequestResourceUriProvider`

  * `RequestResourceUriProvider`

    Return a resource URL to include in policy decision requests to AM, by using the original URI of the request or the `baseURI` of the route.

    ```json
    "resourceUriProvider": {
      "type": "RequestResourceUriProvider",
      "config": {
        "useOriginalUri": configuration expression<boolean>,
        "includeQueryParams": configuration expression<boolean>
      }
    }
    ```

    * `useOriginalUri`: *configuration expression<[boolean](preface.html#definition-boolean)>, optional*

      A flag to use the original URI of the request as the resource URL in policy decision requests to AM.

      * `true`: Use the original URI of the request as the resource URL when requesting policy decisions from AM.

      * `false`: Use the `baseURI` of the route as the resource URL when requesting policy decisions from AM.

      Default: `false`

    * `includeQueryParams`: *configuration expression<[boolean](preface.html#definition-boolean)>, optional*

      A flag to include query parameters in the resource URL when requesting policy decisions from AM:

      * `true`: Include query parameters in the resource URL. For example, use the following URL with a query parameter: `http://ig.example.com:8080/login?demo=capture`.

      * `false`: Exclude query parameters from the resource URL. For example, exclude the query parameter from the previous example: `http://ig.example.com:8080/login`.

        For AM policies that specify resource URLs without query parameters, use this option to reduce the amount of cached information.

      Default: `true`

  * `ScriptableResourceUriProvider`

    Use a script to return a resource URL to include in policy decision requests to AM. The result of the script must be a string that represents the resource URL.

    ```json
    "resourceUriProvider": {
      "type": "ScriptableResourceUriProvider",
      "config": {
        "type": configuration expression<string>,
        "file": configuration expression<string>, // Use either "file"
        "source": [ string, ... ],                // or "source", but not both.
        "args": object,
        "clientHandler": Handler reference
      }
    }
    ```

    For information about these properties, see [Scripts](Scripts.html).

    The following example script replaces existing query parameters with a single parameter:

    ```json
    "resourceUriProvider": {
      "type": "ScriptableResourceUriProvider",
      "config": {
        "type": "application/x-groovy",
        "source": [
          "request.uri.setQuery('fromIG=true')",
          "return request.uri.toASCIIString()"
        ]
      }
    }
    ```

* `"executor"`: *Executor service [reference](preface.html#definition-reference), optional*

  |   |                                                                                                                                                                            |
  | - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | This property is deprecated; use the `cache` subproperty `executor` instead. For more information, refer to [Deprecation](../release-notes/deprecated-functionality.html). |

  An executor service to schedule the execution of tasks, such as the eviction of entries in the cache.

  Default: `ForkJoinPool.commonPool()`

## Example policy enforcement using claimsSubject

This route is a variant of the route in [Enforce AM policy decisions in the same domain](../gateway-guide/pep.html#pep-sso). It enforces a policy decision from AM, using `claimsSubject` instead of `ssoTokenSubject` to identify the subject.

To use this route:

1. Set up AM as described in [Enforce AM policy decisions in the same domain](../gateway-guide/pep.html#pep-sso).

2. In AM, select the policy you created in the previous step, and add a new resource:

   * Resource Type: `URL`

   * Resource pattern: `*://*:*/*`

   * Resource value: `http://app.example.com:8081/home/pep-claims`

3. In the same policy, add the following subject condition:

   * `Any of`

   * Type : `OpenID Connect/JwtClaim`

   * claimName : `iss`

   * claimValue : `am.example.com`

4. Set an environment variable for the IG agent password, and then restart IG:

   ```bash
   $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
   ```

   The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

5. Add the following route to serve static resources, such as .css, for the sample application:

   * Linux

   * Windows

   ```
   $HOME/.openig/config/routes/static-resources.json
   ```

   ```
   %appdata%\OpenIG\config\routes\static-resources.json
   ```

   ```json
   {
     "name" : "sampleapp-resources",
     "baseURI" : "http://app.example.com:8081",
     "condition": "${find(request.uri.path,'^/css')}",
     "handler": "ReverseProxyHandler"
   }
   ```

6. Add the following route to IG:

   * Linux

   * Windows

   ```
   $HOME/.openig/config/routes/04-pep-claims.json
   ```

   ```
   %appdata%\OpenIG\config\routes\04-pep-claims.json
   ```

   ```json
   {
     "name": "pep-claims",
     "baseURI": "http://app.example.com:8081",
     "condition": "${find(request.uri.path, '^/home/pep-claims')}",
     "heap": [
       {
         "name": "SystemAndEnvSecretStore-1",
         "type": "SystemAndEnvSecretStore"
       },
       {
         "name": "AmService-1",
         "type": "AmService",
         "config": {
           "url": "http://am.example.com:8088/openam",
           "agent": {
             "username": "ig_agent",
             "passwordSecretId": "agent.secret.id"
           },
           "secretsProvider": "SystemAndEnvSecretStore-1",
           "version": "7.2"
         }
       }
     ],
     "handler": {
       "type": "Chain",
       "config": {
         "filters": [
           {
             "name": "SingleSignOnFilter-1",
             "type": "SingleSignOnFilter",
             "config": {
               "amService": "AmService-1"
             }
           },
           {
             "name": "PolicyEnforcementFilter-1",
             "type": "PolicyEnforcementFilter",
             "config": {
               "application": "PEP-SSO",
               "claimsSubject": {
                 "sub": "${contexts.ssoToken.info.uid}",
                 "iss": "am.example.com"
               },
               "amService": "AmService-1"
             }
           }
         ],
         "handler": "ReverseProxyHandler"
       }
     }
   }
   ```

7. Go to <http://ig.example.com:8080/home/pep-claims>.

8. Log in to AM as user `demo`, password `Ch4ng31t`.

   AM returns a policy decision that grants access to the sample application.

## More information

[org.forgerock.openig.openam.PolicyEnforcementFilter](../_attachments/apidocs/org/forgerock/openig/openam/PolicyEnforcementFilter.html)

[org.forgerock.openig.openam.PolicyDecisionContext](../_attachments/apidocs/org/forgerock/openig/openam/PolicyDecisionContext.html)

[PolicyDecisionContext](RequestsResponsesContexts.html#PolicyDecisionContext)

AM's [Authorization guide](https://docs.pingidentity.com/pingam/7.2/authorization-guide/index.html)
