Configuring NGINX API micro-gateway to support Open Banking's Advanced FAPI security profile


In my last article, Integrating NGINX Controller API Management with PingFederate to secure financial services API transactions, we have seen how to configure NGINX Controller to perform basic JWT authorization against PingFederate, configured as OIDC IdP / OAuth Autorization Server.

One weakness of the basic JWT authentication mechanism is the lack of context: anyone presenting a valid JWT will be allowed to performed the actions granted by the token, even if the sender is not the original OAuth client that was issued the token. This opens an avenue for attackers to use JWTs stollen from their rightful owners. Ideally, a mechanism of restricting the usage of the JWT to their original requestor is needed and this type of protection is specifically required for API calls presenting the highest risk, such as financial API calls.

For example, Financial-grade API (FAPI) Security Profile 1.0 - Part 2: Advanced (Read and Write API Security Profile) specifies that:

Authorization server:

  • shall only issue sender-constrained access tokens;
  • shall support MTLS as mechanism for constraining the legitimate senders of access tokens;

Uncertainty of resource server handling of access tokens

The protected resources that conform to this document shall not accept a bearer access token. They shall only support sender-constrained access tokens via MTLS.

It is therefore useful to examine the configuration of NGINX, in its micro-gateway deployment mode, needed to perform the function of a resource server in cases requiring the Advanced FAPI security profile.


A high-level diagram of the lab environment used to demonstrate this setup is found below:



The roles performed by each network element are described below:

Authentication and API flow

The workflow is very similar with the one described in my last article, with the differences highlighted here in bold:

  1. The user logs into the Third Party Provider application ("client") and creates a new funds transfer
  2. The TPP application redirects the user to the OAuth Authorization Server / OIDC IdP - PingFederate
  3. The user provides its credentials to PingFederate and gets access to the consent management screen where the required "payments" scope will be listed
  4. If the user agrees to give consent to the TPP client to make payments out of his/her account, PingFederate will generate an authorization code (and an ID Token) and redirect the user to the TPP client
  5. The TPP client opens an MTLS connection to the IdP, authenticates itself with a client certificate, exchanges the authorization code for a user-constrained access token and attaches it as a bearer token to the /domestic-payments call sent to the API gateway over an MTLS session authenticated with the same client certificate
  6. The API Gateway terminates the MTLS session and obtains the client certificate, authenticates the access token by downloading the JSON Web Keys from PingFederate, checks the hashed client certificate matches the value found in the token and grants conditional access to the backend application
  7. The Kubernetes Ingress receives the API call and performs WAF security checks via NGINX App Protect
  8. The API call is forwarded to the backend server pod


Examining the differences between the workflows, it becomes apparent the extra actions NGINX API micro-gateway has to perform to support this advanced security use case are MTLS termination and client certificate hash verification.

NGINX API micro-gateway configuration

The full configuration is available on DevCentral's Code Share: Configure NGINX microgateway for MTLS termination and client certificate hash verification

I will highlight below the most relevant parts of the configuration.

MTLS termination

server {
    listen 443 ssl;
    ssl_certificate /etc/nginx/f5lab.crt;
    ssl_certificate_key /etc/nginx/f5lab.key;
    ssl_session_cache off;
    ssl_prefer_server_ciphers off;

    ssl_client_certificate /etc/nginx/updated_ca.crt;
    ssl_verify_client on;
    ssl_verify_depth 10;

A detailed explanation of each of these commands can be found in the ngx_http_ssl_module user guide.

JWT client certificate hash verification

To compute and validate the client certificate hash, we will use an njs script (more information on njs scripting language and installation process can be found here).

The njs script used (named "x5t.js" in our case) is shown below:

function validate(r) {
    var clientThumbprint = require("crypto")
        .update(Buffer.from(r.variables.ssl_client_raw_cert.replace(/(\n|----|-BEGIN|-END| CERTIFICATE-)/gm, ''), 'base64'))
    return clientThumbprint === r.variables.jwt_cnf_fingerprint ? '1' : '0';

export default { validate }

Importing the "x5t.js" script in the main nginx configuration is done by:

js_import /etc/nginx/x5t.js;

We are populating the value of variable $jwt_cnf_fingerprint (available to the njs script via "r.variables.jwt_cnf_fingerprint") by extracting the 'x5t#S256' value from JWT:

auth_jwt_claim_set $jwt_cnf_fingerprint 'cnf' 'x5t#S256';

The "validate" function of "x5t.js" will the compare the value of $jwt_cnf_fingerprint variable extracted from JWT with the computed SHA256 hash of the client certificate and set the validation result in the $thumbprint_match variable.

js_set $thumbprint_match x5t.validate;

Lastly, we will make a decision to accept or block client's access based on the validation result:

if ($thumbprint_match != 1) {
    return 403 'Access denied because client SSL certificate thumbprint does not match jwt_cnf_fingerprint';


Supporting MTLS termination and client certificate hash validation against sender-constrained JWTs issued by Authorization Servers such as PingFederate, enables NGINX API micro-gateway to support Open Banking's Advanced FAPI security profile.


The UDF lab environment used to build this configuration can be found here.

Published Nov 01, 2021
Version 1.0

Was this article helpful?

No CommentsBe the first to comment