Open Banking Brazil DCR Request Validation in Nginx

Open Banking Brazil DCR Request Validation in Nginx

Code Examples / api-integration

This code example provides a proof of concept on how to validate a Dynamic Client Request when the validation of the Curity Identity Server does not satisfy the requirements. The example is based on the Open Banking Brazil Dynamic Client Registration Profile which specifies a software statement that nginx acts upon. It implements the approach described in Open Banking Brazil DCR Request Validation.

Open Banking Brazil DCR Request Validation Approach

The logic is divided into two files:

  • dcr-request.lua
  • nginx.conf

Lua scripting is central in this example. The lua module in dcr-request.lua includes several validation functions:

  • validate_client_metadata
  • validate_software_statement
  • validate_scopes_for_roles

The first function validates the fields of the client metadata that are not part of the software statement. For example, the Open Banking Brazil Dynamic Client Registration Profile (OBB DCR Profile) clearly states that jwks must not be included in the request. validate_software_statement forwards the software statement to an external service that validates the signature of the software statement JWT. The reason an external service is consulted is a limitation in the resty.jwt module that does not support the PS256 signing algorithm that is mandatory by the OBB DCR Profile. If the service returns with a success status, in this case HTTP 204, nginx parses the software statement and checks that the requested scopes match the roles in the statement. If any scopes are missing, it will add them to the request before forwarding it to the Curity Identity Server.

The module is loaded within an access_by_lua_block directive in the nginx.conf file.

access_by_lua_block {
...

  if http_method == "POST" then
    -- Load request data
    ngx.req.read_body()
    local http_body_data = ngx.req.get_body_data()
  
    -- Call module that handles the request validation and returns updated client metadata
    metadata_valid, return_value = pcall(dcr_request.validate, http_body_data)
  end
  
  -- Exit access phase
  if metadata_valid == true then
    -- Update request body
    ngx.req.set_body_data(return_value)
    ngx.log(ngx.DEBUG, "Request Body: " .. return_value)
    ngx.exit(ngx.OK)
  else
    ngx.log(ngx.ERR, "Request validation failed: " .. return_value)
    ngx.status = ngx.HTTP_BAD_REQUEST
    ngx.say(cjson.encode({
      error = "invalid_client_metadata",
      error_description = return_value
      }))
    return ngx.exit(ngx.HTTP_BAD_REQUEST)
  end
}

Proof Of Concept

This is example is just a proof of concept and is by no means a complete implementation of the Open Banking Brazil Dynamic Client Registration Profile (OBB DCR Profile). Nevertheless, it provides ideas and code snippets for how to validate the software statement. It also shows how to update the request based on the content of the software statement.

The OBB DCR Profile requires mTLS client authentication. The session is terminated at the proxy. As a result, the Curity Identity Server is configured with mutual-tls-by-proxy.

Mutual TLS with Proxy Termination

This setting requires nginx to forward the client certificate in an HTTP header. The name of the header is configurable. This example forwards the certificate in the header called X-Client-SSL-Cert.

# SSL settings
ssl_certificate      /tmp/server.cert.pem;
ssl_certificate_key  /tmp/server.key.pem;
# Trusted issuers for client certificates
ssl_client_certificate /tmp/trusted-client-cert-issuers.pem;
ssl_verify_client on;

...

# Forward request
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Add client certificate
proxy_set_header X-Client-SSL-Cert $ssl_client_escaped_cert;
proxy_pass http://internal-curity-runtime:8443;

Keep up with our latest articles and how-tos RSS feeds.