OAuth2 Introspection Policy

API Owners can restrict access to their APIs with OAuth2 tokens. The policy is configured to grant access to APIs after having tokens introspected.

Overview

NGINX Management Suite API Connectivity Manager (ACM) API Owners can restrict access to their APIs with OAuth2 tokens by swapping an opaque token for claims or JWT token to be proxied to the backend service. The policy can be configured to grant access to APIs after having the tokens introspected. In addition, the claims in the token can be extracted and forwarded to the backend service.

What is OAuth2?

The OAuth2 Authorization Framework [RFC-6749] grants third-party applications limited access to an HTTP service either by orchestrating an approval interaction between the resource owner and the HTTP service or granting the third-party application access on its own behalf.

OAuth2 is an authorization protocol, not an authentication protocol. As such, OAuth2 grants access to a set of resources, for example, internal APIs or user data.

OAuth2 Roles

The idea of roles is part of the core specification of the OAuth2 Authorization Framework. These define the essential components of an OAuth2 system:

  • Resource Owner: An entity capable of granting access to a protected resource. It could be a system or an end-user.
  • Client: An application making protected resource requests on behalf of the Resource Owner and with its authorization.
  • Authorization Server: The server that issues access tokens to the client after successfully authenticating the resource owner and obtaining authorization. The authorization server exposes two endpoints: the Token endpoint, which is involved in a machine-to-machine interaction for issuing access tokens, and the Introspection endpoint, which is used by the Resource Server to validate client access tokens.
  • Resource Server: The server protecting the user resources capable of accepting and responding to protected resource requests using access tokens. In this guide, NGINX running within the ACM API-Proxy is the Resource Server.

Token Introspection

The standard method for validating access tokens with an IdP is called Token Introspection. OAuth2 Token Introspection [RFC 7662] is now a widely supported standard that describes a JSON/REST interface that a Resource Server uses to present a token to the IdP, and describes the structure of the response. It is supported by many of the leading IdP vendors and cloud providers.

NGINX can be used to validate access tokens on behalf of backend services. This has several benefits:

  • Requests reach the backend services only when the client has presented a valid token
  • Existing backend services can be protected with access tokens without requiring code changes
  • Only the NGINX instance (not every app) needs to be registered with the IdP
  • Behavior is consistent for every error condition, including missing or invalid tokens

The OAuth2 Token Introspection flow includes the following steps:

OAuth2 Token Introspection Flow.

Set up OAuth2 Introspection Policy

You can set up OAuth2 Introspection policy by using either the web interface or the REST API.

Edit the API-Proxy Settings

  1. In the ACM user interface, go to Services > API Proxies click the icon in the Actions column for the API proxy that you want to enable the OAuth2 Introspection policy for, select Edit Proxy.
  2. Under the Advanced section select Policies.
  3. Under the API Proxy tab, locate the OAuth2 Introspection policy and click the icon, select Add Policy.
  4. Update Client Request settings.
Configuration Setting Description
Specify location of token in incoming user request Specifies where the access token is supplied in the incoming user request the key under which the access token can be extracted from. Default behaviour is as a Bearer token in the Authorization request header.
  1. Update Introspection Request settings.
Configuration Setting Description
Enter the introspection endpoint The IdP OAuth2 Token Introspection endpoint [RFC 7662] where NGINX IdP client will send client access_token.
Specify the introspection response type Whether the token introspection endpoint should respond with a JSON object or JSON Web Token (JWT). Default is application/json.
Specify list of claims to forward as headers to backend Forward claims from the token introspection response in the proxy header to the backend service.

Can only be applied if the introspection response is configured to application/json.
Enable JWT token forwarding to backend Forward introspection token response to backend service.

Can only be applied if the introspection response is configured to application/jwt.
Specify how long introspected tokens will be cached Specifies how long the introspected tokens will be cached. Tokens will be refreshed from the URI endpoint after the duration. Set as 0 to disable.

Follows NGINX time measurement syntax.
Specify OAuth2 Token Type Hint A hint about the type of the token submitted for introspection. The protected resource can pass this parameter to help the authorization server optimize the token lookup. Values for this field are defined in [RFC6749].
  1. Update Credentials.
Configuration Setting Description
Enter Client Application ID Identifies the IdP Client who is making the token introspection request.
Enter Client Secret/Password The IdP Client secret/password.
  1. Enable Resolver if external DNS required.
Configuration Setting Description
Time Out By default, NGINX caches answers using the TTL value of a response. The valid parameter allows overriding it.

Follows NGINX time measurement syntax.
Valid For Sets a timeout for name resolution.

Follows NGINX time measurement syntax.
Hostname The DNS Hostname or IP Address. Multiple DNS Resolvers can be added for a given OAuth2 Introspection Policy.
Listened Port The DNS Port number
  1. Update Error Handling.
Configuration Setting Description
Specify authorization failed error code The error code that needs to be used by the nginx data plane when the backend service cannot find token match or access is forbidden.
Specify authorization token not provided error code The error code that needs to be used by the nginx data plane when the backend service when token is not supplied.
  1. Select Add.
  2. Select Save and Submit your changes.

  1. Send a POST request to add the OAuth2 Introspection policy to the API-Proxy.
Method Endpoint
POST /services/workspaces/<SERVICE_WORKSPACE_NAME>/proxies
Note:
While all request body configuration values are presented in the request body structure example below, not all configuration values are compatible. Please see the configuration value description table for further information.
{
    "name": "{{proxyName}}",
    "version": "v1",
    "proxyConfig": {
        "hostname": "{{environmentHostname}}",
        "ingress": {
            "basePath": "/api"
        },
        "backends": [
            {
                "serviceName": "backend-svc",
                "serviceTargets": [
                    {
                        "hostname": "10.0.0.10"
                    }
                ]
            }
        ],
        "policies": {
            "oauth2-introspection": [
                {
                    "action": {
                        "introspectionEndpoint": "https://example.idp.com:8443/oauth/v2/oauth-introspect",
                        "introspectionResponse": "application/json",
                        "cacheIntrospectionResponse": "5m",
                        "clientTokenSuppliedIn": "HEADER",
                        "clientTokenName": "Authorization",
                        "authzServerTokenHint": "ACCESS_TOKEN",
                        "forwardToken": false,
                        "forwardedClaimsInProxyHeader": [
                            "username",
                            "exp",
                            "scope"
                        ],
                        "resolver": {
                            "valid": "30s",
                            "timeout": "30s",
                            "servers": [
                                {
                                    "hostname": "example.com"
                                },
                                {
                                    "hostname": "10.0.0.11",
                                    "port": 53
                                }
                            ]
                        },
                        "errorReturnConditions": {
                            "noMatch": {
                                "returnCode": 403
                            },
                            "notSupplied": {
                                "returnCode": 401
                            }
                        }
                    },
                    "data": [
                        {
                            "clientAppID": "idp-client-app-id",
                            "clientSecret": "dbdaa3e1-f100-420x-bfd0-875bd8a77cd7"
                        }
                    ]
                }
            ]
        }
    }
}
Field Datatype Possible Values Description Required Default value
introspectionEndpoint string Example:
"https://idp.com/introspect"
The IdP OAuth2 Token Introspection endpoint [RFC 7662] where NGINX IdP client will send client access_token. True N/A
introspectionResponse string One of:
["application/json",
"application/jwt"]
Whether the token introspection endpoint should respond with a JSON object or JSON Web Token (JWT). False "application/json"
cacheIntrospectionResponse string Example: "5m" Specifies how long the introspected tokens will be cached. Tokens will be refreshed from the URI endpoint after the duration. Set as `0s m h` to disable.

Follows NGINX time measurement syntax.
clientTokenSuppliedIn string One of:
["HEADER", "QUERY"]
Specifies where the access token is supplied in the incoming user request. False "HEADER"
clientTokenName string Example:
"Authorization"
Specifies the key under which the access token can be extracted from in the incoming user request.

Note: To maintain Bearer token behaviour, clientTokenSuppliedIn must be set to HEADER and clientTokenName must be set to Authorization. This is the default behaviour of the Introspection Policy.
False "Authorization"
authzServerTokenHint string One of:
["ACCESS_TOKEN",
"REFRESH_TOKEN"]
A hint about the type of the token submitted for introspection. The protected resource can pass this parameter to help the authorization server optimize the token lookup. Values for this field are defined in [RFC6749]. False N/A
forwardToken boolean true/false Forward introspection token response to backend service.

Can only be applied if the introspectionResponse is set to application/jwt.
False true
forwardedClaimsInProxyHeader array of strings Standard claims can be found in
OAuth2 Token Introspection
[RFC 7662].

This is not an exhaustive list,
IdPs and Resource Owners can
configure their own claims.
Forward claims from the token introspection response in the proxy header to the backend service.

Can only be applied if the introspectionResponse is set to application/json.
False ["scope",
"username",
"exp"]
resolver.valid string Example: "30s" By default, NGINX caches answers using the TTL value of a response. The valid parameter allows overriding it.

Follows NGINX time measurement syntax.
False "30s"
resolver.timeout string Example: "30s" Sets a timeout for name resolution.

Follows NGINX time measurement syntax.
False "30s"
resolver.servers[].hostname string Valid hostname or IP Address The DNS Hostname. True N/A
resolver.servers[].port int32 Valid 32-bit integer The DNS Port number. False N/A
errorReturnConditions.noMatch integer In range: 400 - 599 The error code that needs to be used by the nginx data plane when the backend service cannot find token match or access is forbidden. False 403
errorReturnConditions.notSuppplied integer In range: 400 - 599 The error code that needs to be used by the nginx data plane when the backend service when token is not supplied. False 401
data.clientAppID string Example:
"nginx-docs-client"
Identifies the IdP Client who is making the token introspection request. True N/A
data.clientSecret string Example:
"db3e1-f100-420x-bfd0"
The IdP Client secret/password. True N/A

Policy Interoperability Considerations

It is only possible to configure one OAuth2 Introspection Policy per Proxy in ACM. Only one set of clientAppId credentials can be configured per OAuth2 Introspection Policy.

While an OAuth2 Introspection policy is configured for a Proxy in ACM it is not possible to configure any of the following policies on the same Proxy:

  1. API Key Authentication
  2. Basic Authentication
  3. JWT Assertion

Similarly, if any of the above three policies are configured for a Proxy in ACM, it is not possible to additionally configure an OAuth 2.0 Introspection Policy.

Security Considerations

Token Caching

Consumers of the introspection endpoint may wish to cache the response of the endpoint for performance reasons. As such, it is important to consider the performance and security trade-offs when deciding to cache the values. For example, shorter cache expiration times will result in higher security since the resource servers will have to query the introspection endpoint more frequently, but will result in an increased load on the endpoint. Longer expiration times leave a window open where a token may actually be expired or revoked, but still be able to be used at a resource server for the remaining duration of the cache time.

One way to mitigate this problem is for consumers to never cache the value beyond the expiration time of the token, which would have been returned in the “exp” parameter of the introspection response.

JWT Introspection Responses

The introspection response type application/jwt, configured through action.introspectionResponse, has not had its security protocol specification finalised at the time of development and writing it remains in DRAFT state. The draft specification JWT Response for OAuth Token Introspection can be found here.

While in this state the specification is likely to change at any time, and how we implement it in ACM may change to meet the requirements of the specification. We recommend that the default OAuth2 Introspection response type application/json is used for all production scenarios.