When creating APIs it can be useful to separate out the concern of validating JWT tokens to some downstream service. This has a number of benefits

You can still pass and make use of JWTs and their payloads within the API but you just offload the task of actually validating that payload to something else.

This sort of functionality, along with things like mutual TLS and other security concerns are starting to become commonplace in service mesh implementations such as Istio.

My needs are slightly different as I am looking to have this in a multi-environment setup primarily using Cloud Foundry and Docker Swarm.

After a little bit of searching looking for a proxy that was able to do this easily, I settled upon Envoy. This was my first time having to get to grips with Envoy so took a little bit of reading and understanding how it all fits together and more than a little bit of trial and error.

How to configure?

I needed to validate JWT tokens on only certain paths and I wanted to validate based on keys that came from the JWKS URL of an OIDC compatible provider.

The configuration comes in a number of sections

The HTTP filter

Inside your config.yml you need to add a new HTTP filter. This needs to be above the envoy.router filter and anything else that you might have in there that you want the security to run before.

http_filters:
  - name: envoy.filters.http.jwt_authn
    config:
      providers:
        oidc_provider:
          issuer: my_issuer_name
          audiences:
            - audience_to_validate
          forward: "true"
          remote_jwks:
            http_uri:
              uri: https://my.provider/jwks_url
              cluster: identity_provider
              timeout: 5s
            cache_duration:
              seconds: 300
      rules:
        - match: { prefix: /api }
          requires:
            provider_name: oidc_provider
      bypass_cors_preflight: "true"

To explain this config

Cluster Configuration

In my case, I wanted to automatically retrieve the latest signing keys from the JWKS URL of my OIDC IdP. To enable this you need to create a cluster specifically for the IdP domain and port.

clusters:
  - name: identity_provider
    connect_timeout: 0.25s
    type: STRICT_DNS
    load_assignment:
      cluster_name: identity_provider
      endpoints:
        - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: my.provider
                    port_value: 443
    transport_socket:
      name: envoy.transport_sockets.tls

This cluster has the same name as the cluster identified in the remote_jwks configuration above. Here we set the endpoint of this cluster to be the domain of the URL of the IdP configured above. We also enable SSL with the transport_socket config.