---
title: PolicyEnforcementFilter
description: Requests and enforces policy decisions from AM. For more information, refer to PingGateway's PingAM policy enforcement and AM's Authorization guide.
component: pinggateway
version: 2026
page_id: pinggateway:reference:PolicyEnforcementFilter
canonical_url: https://docs.pingidentity.com/pinggateway/2026/reference/PolicyEnforcementFilter.html
revdate: 2026-01-15
section_ids:
  PolicyEnforcementFilter-conf-am: Notes on configuring policies in AM
  pef-eviction: WebSocket notifications for policy changes
  PolicyEnforcementFilter-usage: Usage
  PolicyEnforcementFilter-properties: Properties
  PolicyEnforcementFilter-amService: amService
  PolicyEnforcementFilter-pepRealm: pepRealm
  PolicyEnforcementFilter-ssoTokenSubject: ssoTokenSubject
  PolicyEnforcementFilter-jwtSubject: jwtSubject
  PolicyEnforcementFilter-claimsSubject: claimsSubject
  PolicyEnforcementFilter-cache: cache
  PolicyEnforcementFilter-application: application
  PolicyEnforcementFilter-environment: environment
  PolicyEnforcementFilter-failureHandler: failureHandler
  PolicyEnforcementFilter-resourceUriProvider: resourceUriProvider
  PolicyEnforcementFilter-authenticateResponseRequestHeader: authenticateResponseRequestHeader
  PEF-example: Examples
  PolicyEnforcementFilter-moreinfo: More information
---

# PolicyEnforcementFilter

Requests and enforces policy decisions from AM. For more information, refer to PingGateway's [PingAM policy enforcement](../gateway-guide/policy-enforcement.html) and AM's [Authorization guide](https://docs.pingidentity.com/pingam/8.1/am-authorization/).

AM can deny access with *advice* in the policy decision response. The advice indicates how to authenticate the user for access to the resource. PingGateway stores attributes and advices in the `policyDecision` context. For information, refer to [PolicyDecisionContext](PolicyDecisionContext.html).

When you precede a PolicyEnforcementFilter in a [Chain](Chain.html) with a [SingleSignOnFilter](SingleSignOnFilter.html) or [CrossDomainSingleSignOnFilter](CrossDomainSingleSignOnFilter.html), PingGateway reacts to AM advices in the policy decision:

* `AuthLevelConditionAdvice`: Authenticate the user to reach the specified authentication level.

* `AuthSchemeConditionAdvice`: Authenticate the user based on the details of the specified scheme.

* `AuthenticateToRealmConditionAdvice`: Authenticate the user to the specified realm.

* `AuthenticateToServiceConditionAdvice`: Authenticate the user with the specified authentication journey or chain. (AM has deprecated authentication chains.)

* `TransactionConditionAdvice`: Perform additional actions to authenticate the user for one-time access to the protected resource.

To respond to advices from AM:

* Use the PolicyEnforcementFilter in a Chain.

* Add a SingleSignOnFilter or CrossDomainSingleSignOnFilter before the PolicyEnforcementFilter.

If you don't precede it with an SSO filter, the PolicyEnforcementFilter ignores the advice. When AM returns advice, the PolicyEnforcementFilter denies access with HTTP 403 Forbidden by default.

If you can't use a SingleSignOnFilter or CrossDomainSingleSignOnFilter before the PolicyEnforcementFilter, it's still possible to enable PingGateway to respond to advices:

* Write a script to respond to advice from AM.

  For details about the advice types AM returns, refer to [Policy decision advice](https://docs.pingidentity.com/pingam/8.1/am-authorization/rest-api-authz-policy-decisions.html#rest-api-authz-policy-decision-advice) in the AM documentation.

* Use a scriptable filter or handler in the `"failureHandler"` of the PolicyEnforcementFilter to trigger the script.

## 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 PingGateway 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, PingGateway receives a notification from AM when a policy decision is created, deleted, or updated.

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

## Usage

```none
{
  "name": string,
  "type": "PolicyEnforcementFilter",
  "config": {
    "amService": AmService reference,
    "pepRealm": configuration expression<string>,
    "ssoTokenSubject": runtime expression<string>,
    "jwtSubject": runtime expression<string>,
    "claimsSubject": map or runtime expression<map>,
    "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>
    },
    "application": configuration expression<string>,
    "environment": map or runtime expression<map>,
    "failureHandler": Handler reference,
    "resourceUriProvider": ResourceUriProvider reference,
    "authenticateResponseRequestHeader": configuration expression<string>
  }
}
```

## Properties

### amService

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

The AM instance to use for policy decisions.

### pepRealm

`"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

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

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

`ssoTokenSubject` can take the value of the session token from the following sources:

* When the PolicyEnforcementFilter is preceded by a SingleSignOnFilter, `${contexts.ssoToken.value}`.

* When the PolicyEnforcementFilter is preceded by a CrossDomainSingleSignOnFilter, `${contexts.ssoToken.value}` or `${contexts.cdsso.token}`.

* When the PolicyEnforcementFilter isn't preceded by a SingleSignOnFilter or CrossDomainSingleSignOnFilter, `ssoTokenSubject` usually points to the token value.

  The token value can be in the request message, a header, or a cookie. For example, the `ssoTokenSubject` can point to a header value, such as `${request.headers.cookie name}`, where `cookie name` is the AM session cookie name.

  Requests that return a policy decision with advices fail with an HTTP 403 and no advice handling.

### jwtSubject

`"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](AuthorizationCodeOAuth2ClientFilter.html) before the PEP filter and use `${contexts.oauth2Info.idToken}` as the [expression](Expressions.html) value.

### claimsSubject

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

A map of one or more data pairs with the format `Map<String, Object>`, where:

* The key is the name of a claim.

* The value is a claim object or a runtime expression that evaluates to a claims object.

The following formats are allowed:

```none
{
  "claimsSubject": {
    "string": "runtime expression<object>",
    ...
  }
}
```

```none
{
  "claimsSubject": "runtime expression<map>"
}
```

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

In the following example, the property is a map whose first value is a runtime [expression](Expressions.html#Expressions) that evaluates to a JWT claim for the subject, and whose second value is a JWT claim for the subject:

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

In the following example, the property is a runtime [expression](Expressions.html#Expressions) that evaluates to a map with the format `Map<String, Object>`:

```none
"claimsSubject": "${contexts.oauth2Info.idTokenClaims}"
```

Learn more in the example of [policy enforcement using claimsSubject](#PEF-example).

### cache

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

Enable and configure caching of policy decisions from AM based on [Caffeine](https://github.com/ben-manes/caffeine).

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

```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>
  }
}
```

|   |                                                         |
| - | ------------------------------------------------------- |
|   | 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.

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

Default: Policy decisions aren't cached.

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

  Enable or disable caching of policy decisions.

  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 the policy decision remains valid, PingGateway 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`, PingGateway 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 PingGateway receives no notifications for AM events. If the cache isn't cleared it can become outdated and PingGateway 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 aren't cached, but don't update the cache with these requests.

    * When the notification service is reconnected:

      * Continue to use the existing cache.

      * Query AM for incoming requests that aren't 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 don't update the cache with these requests.

    * When the notification service is reconnected:

      * Query AM for all requests that aren't 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 aren't cached, but don't update the cache with these requests.

    * When the notification service is reconnected:

      * Query AM for all requests that aren't 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`

### application

`"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

### environment

`"environment"`: *[map](preface.html#definition-map) or runtime expression\<map>, optional*

A map of one or more data pairs with the format `Map<String, Object>`, where:

* The key is the name of a field in the request environment or context, such as a request header.

* The value is the object to forward to AM with a policy decision request or a runtime expression that evaluates to the object.

The following formats are allowed:

```none
{
  "claimsSubject": {
    "string": "runtime expression<object>",
    ...
  }
}
```

```none
{
  "claimsSubject": "runtime expression<map>"
}
```

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

Forward any HTTP header or any value that the AM policy definition can use.

In the following example, the property is a map whose values are runtime [expressions](Expressions.html#Expressions) that evaluate to request headers, an ID token, and the IP address of the subject making the request:

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

### failureHandler

`"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` passes the request to a `StaticResponseHandler` to display a message:

```none
"failureHandler": {
    "type": "StaticResponseHandler",
    "config": {
        "status": 403,
        "headers": {
            "Content-Type": [
                "text/plain; charset=UTF-8"
            ]
        },
        "entity": "Restricted area. You don't have sufficient privileges."
    }
}
```

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

### resourceUriProvider

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

Use one of the following providers to return a resource URL to include in policy decision requests to AM:

* [RequestResourceUriProvider](RequestResourceUriProvider.html)

* [ScriptableResourceUriProvider](ScriptableResourceUriProvider.html)

The PolicyEnforcementFilter uses the returned resource URL to identify the policy decision in the policy cache.

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

Default: `RequestResourceUriProvider` configured to use the request URI with all query parameters included.

Maximize the cache hit ratio by managing the returned resource URL in conjunction with AM policies:

* Strip all query parameters from the returned resource URL

  Consider the following AM policy that matches requests on the specified path. The policy ignores query parameters:

  ```none
  https://ig.example.com:8443/app
  ```

  The following requests match the path but have additional query parameters:

  ```none
  https://ig.example.com:8443/app?day=monday
  https://ig.example.com:8443/app?day=monday&place=london
  https://ig.example.com:8443/app?day=monday&place=london&building=x
  ```

  When `includeQueryParams` in RequestResourceUriProvider is `true`, the ResourceUriProvider includes all query parameters in requests for policy decisions. The PolicyEnforcementFilter requests a policy desicion for the first request `/app?day=monday` and caches the descision. The second request `app?day=monday&place=london` doesn't match the cached decision, so the PolicyEnforcementFilter requests another policy decision and adds it to the cache. Similarly for the third request.

  When `includeQueryParams` in RequestResourceUriProvider is `false`, the ResourceUriProvider strips all query parameters from the requests. The PolicyEnforcementFilter requests a policy decision for the first request without query parameters and caches the policy desicion. The following two requests without query parameters match the cached decision and PingGateway uses the cached decision without consulting AM.

* Include only specified query parameters in the returned resource URL

  Consider a similar example where an AM policy matches requests on the specified path and having a specified query parameter:

  ```none
  https://ig.example.com:8443/app?day=monday
  ```

  The following requests match the path and query parameter, but two of them have additional query parameters:

  ```none
  https://ig.example.com:8443/app?day=monday
  https://ig.example.com:8443/app?day=monday&place=london
  https://ig.example.com:8443/app?day=monday&place=london&building=x
  ```

  Because the policy requires a query parameter, you can't use RequestResourceUriProvider to strip all query parameters from the requests.

  Instead, use a ScriptableResourceUriProvider to include the `?day=monday` query parameter but strip all other query parameters.

  |   |                                                                                                                                                       |
  | - | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | Query order is important. The following queries are semantically the same but don't match: `?day=monday&place=london` and `?place=london&day=monday`. |

  ```none
  "resourceUriProvider": {
    "type": "ScriptableResourceUriProvider",
    "config": {
      "type": "application/x-groovy",
      "source": [
        "// Define a list of parameters to keep",
        "def keepOnly = { [ 'place', 'day' ].contains(it.key) }",
        "// Build a new URI based on the original request URI",
        "return new MutableUri(request.uri).with { uri ->",
        "  // Build a filtered and normalized query string",
        "  uri.rawQuery = new Form().with { form ->",
        "    // Keep only the wanted parameters and sort by name",
        "    form.addAll(request.queryParams.findAll(keepOnly).sort())",
        "    return form.toQueryString()",
        "  }",
        "  // Return the full modified URI",
        "  return uri.toASCIIString()",
        "}"
      ]
    }
  }
  ```

### authenticateResponseRequestHeader

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

A header to include in a request to manage the way PingGateway handles policy advices from AM. The header name and value is case-insensitive. The header value can be set as follows:

* `HEADER`: Return policy advices in a `WWW-Authenticate` header as base64-encoded JSON in a parameter called `advices`.

* Any other value: Return policy advices as parameters in a redirect response (default).

Learn more in the example showing how to [deny requests with advice in a header](../gateway-guide/policy-enforcement.html#about-pep-advices-header).

Default: `x-authenticate-response`

## Examples

You can find examples of policy enforcement in [PingAM policy enforcement](../gateway-guide/policy-enforcement.html).

## 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](PolicyDecisionContext.html)

AM's [Authorization](https://docs.pingidentity.com/pingam/8.1/am-authorization/) documentation
