On this page
This tutorial explains how to integrate an NGINX module that introspects access tokens according to RFC 7662, producing a Phantom Token that can be forwarded to back-end APIs and web services. This module, when enabled, filters incoming requests, denying access to those which do not have a valid OAuth access token presented in an Authorization
header. From this header, the access token is extracted and introspected using the configured endpoint. The JWT obtained from the introspection endpoint replaces the access token in the header of the request that is forwarded by NGINX to the back-end. If the token is not valid or absent, no request to the back-end is made and the caller is given a 401, unauthorized, error.
Configure the Curity Identity Server
In the Curity Identity Server you must configure a client that uses introspection. The following example XML configuration contains an introspection client and also an API client that uses the Client Credentials flow. You could save the configuration to an XML file, update the id
to match that of your token profile if required, then use the Admin UI to Import the Configuration.
<config xmlns="http://tail-f.com/ns/config/1.0"><profiles xmlns="https://curity.se/ns/conf/base"><profile><id>token-service</id><type xmlns:as="https://curity.se/ns/conf/profile/oauth">as:oauth-service</type><settings><authorization-server xmlns="https://curity.se/ns/conf/profile/oauth"><client-store><config-backed><client><id>api-gateway-client</id><client-name>api-gateway-client</client-name><secret>Password1</secret><capabilities><introspection/></capabilities></client><client><id>api-client</id><client-name>api-client</client-name><secret>Password1</secret><audience>api.example.com</audience><scope>read</scope><capabilities><client-credentials/></capabilities></client></config-backed></client-store></authorization-server></settings></profile></profiles></config>
Collect NGINX Resources
To deploy the NGINX phantom token module, download a library .so
file from the GitHub Releases Page to match your version of NGINX and the operating system version on which it runs. You then need to configure settings against API routes and create an introspection route. Start with resources like those listed below.
Resource | Description |
---|---|
alpine.ngx_curity_http_phantom_token_module_1.27.4.so | The module for NGINX 1.27.4 for Alpine Linux. |
nginx.conf | The main NGINX configuration file. |
default.conf.template | An NGINX configuration template that expands environment variables. |
Load the Phantom Token Module
Deploy the module to the modules folder of the NGINX system, such as /usr/lib/nginx/modules
. Then add a line to the main nginx.conf
file to load the phantom token module:
user nginx;worker_processes auto;error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid;load_module modules/ngx_curity_http_phantom_token_module.so;
Configure NGINX Routes
Next, update the default.conf.template
file, to define one or more API routes called by clients and to configure an internal route for introspection. For the internal route you must configure the fixed headers for the introspection request, shown in the below curity
section. Also configure settings to cache introspection responses and the path to the introspection endpoint of the Curity Identity Server.
proxy_cache_path cache levels=1:2 keys_zone=api_cache:10m max_size=10g inactive=60m use_temp_path=off;server {server_name ${NGINX_HOST};listen ${NGINX_PORT};location / {root /usr/share/nginx/html;index index.html index.htm;}location /api {phantom_token on;phantom_token_introspection_endpoint curity;proxy_pass ${API_HOST};}location curity {internal;proxy_pass_request_headers off;proxy_set_header Accept "application/jwt";proxy_set_header Content-Type "application/x-www-form-urlencoded";proxy_set_header Authorization "Basic ${INTROSPECTION_CLIENT_CREDENTIAL}";proxy_cache_methods POST;proxy_cache api_cache;proxy_cache_key $request_body;proxy_ignore_headers Set-Cookie;proxy_pass ${INTROSPECTION_ENDPOINT};}}
The values in curly brackets are environment variables and include a base64 encoded introspection basic credential. You can produce such a credential from an introspection client ID and secret using a scripted command.
export INTROSPECTION_CREDENTIAL="$(echo -n 'api-gateway-client:Password1' | base64)"
Test the Phantom Token Flow
The example API client could get an opaque access token from the Curity Identity Server with a command similar to the following.
curl -u 'api-client:Password1' -X POST https://login.example.com/oauth/v2/oauth-token \-d grant_type=client_credentials \-d scope=read
You could extract the opaque access token from the response and run a second HTTP request to send the access token through NGINX to the API:
curl -H "Authorization: Bearer $OPAQUE_ACCESS_TOKEN" https://api.example.com
The API then receives a JWT access token and uses its claims for authorization.
Example Deployment
If you do not yet have an NGINX system, you can get up and running with a Docker NGINX system. To understand the deployment approach, start with a minimal docker-compose.yml
file.
services:nginx:image: nginx:1.27.4-alpineports:- 8080:80
Then run NGINX with the following deployment command.
docker compose up --force-recreate
From the running system, copy the default configuration files locally and then update the configuration.
CONTAINER_ID=$(docker ps | grep nginx | awk '{print $1}')docker cp $CONTAINER_ID:/etc/nginx/conf.d/default.conf ./default.conf.templatedocker cp $CONTAINER_ID:/etc/nginx/nginx.conf ./nginx.conf
Then update the docker-compose.yml
file with the following settings and re-run the deployment command.
services:nginx:image: nginx:1.27.4-alpineports:- 8080:80volumes:- ./alpine.ngx_curity_http_phantom_token_module_1.27.4.so:/usr/lib/nginx/modules/ngx_curity_http_phantom_token_module.so- ./nginx.conf:/etc/nginx/nginx.conf- ./default.conf.template:/etc/nginx/templates/default.conf.templateenvironment:NGINX_HOST: localhostNGINX_PORT: 80API_HOST: https://api.example.comINTROSPECTION_CLIENT_CREDENTIAL: YXBpLWdhdGV3YXktY2xpZW50OlBhc3N3b3JkMQ==INTROSPECTION_ENDPOINT: https://login.example.com/oauth/v2/oauth-introspect
If you look at the configuration of the running system you will see expanded environment variables.
CONTAINER_ID=$(docker ps | grep nginx | awk '{print $1}')docker exec -it $CONTAINER_ID sh -c "cat /etc/nginx/conf.d/default.conf"
Single Page Applications
For details on how to integrate the phantom token module into API solutions for Single Page Applications (SPA), and to deal with Cross Origin Resource Sharing (CORS) during requests to APIs, see the following links:
Conclusion
You can configure and deploy the Curity module to quickly implement the phantom token pattern in NGINX. Work out the deployment details on a development computer and then deploy in the same way to real environments. The phantom token approach improves privacy, since no sensitive access token details are exposed to internet clients.
Join our Newsletter
Get the latest on identity management, API Security and authentication straight to your inbox.
Start Free Trial
Try the Curity Identity Server for Free. Get up and running in 10 minutes.
Start Free Trial