NGINX Documentation

Setting up JWT Authentication

This article explains how to control authentication of your web resources using JWT authentication.

Introduction

With NGINX Plus it is possible to control access to your resources using JWT authentication. JWT is data format for user information in the OpenID Connect standard, which is the standard identity layer on top of the OAuth 2.0 protocol. Deployers of APIs and microservices are also turning to the JWT standard for its simplicity and flexibility. With JWT authentication, a client provides a JSON Web Token, and the token will be validated against a local key file or a remote service.

Prerequisites

NGINX Plus supports both types of JWT:

  • JSON Web Signature (JWS) - the contents of JWT is digitally signed. The following algorithms can be used for signing:

    • HS256, HS384, HS512
    • RS256, RS384, RS512
    • ES256, ES384, ES512
    • EdDSA (Ed25519 and Ed448 signatures)
  • JSON Web Encryption (JWE) - the contents of JWT is encrypted. The following content encryption algorithms (the “enc” field of JWE header) are supported:

    • A128CBC-HS256, A192CBC-HS384, A256CBC-HS512
    • A128GCM, A192GCM, A256GCM

    The following key management algorithms (the “alg” field of JWE header) are supported:

    • A128KW, A192KW, A256KW
    • A128GCMKW, A192GCMKW, A256GCMKW
    • dir - direct use of a shared symmetric key as the content encryption key

Configuring NGINX Plus to Authenticate API

Let’s assume that NGINX Plus serves as a gateway (proxy_pass http://api_server) to a number of API servers (the upstream {} block), and requests passed to the API servers should be authenticated:

upstream api_server {
    server 10.0.0.1;
    server 10.0.0.2;
}

server {
    listen 80;

    location /products/ {
        proxy_pass http://api_server;
        #...
    }
}

To implement JWT for authentication:

  1. First, it is necessary to create a JWT that will be issued to a client. You can use your identity provider (IdP) or your own service to create JWTs. For testing purposes, you can create your own JWT, see Authenticating API Clients with JWT and NGINX Plus blog post for details.

  2. Configure NGINX Plus to accept JWT: specify the auth_jwt directive that enables JWT authentication and also defines the authentication area (or “realm”, “API” in the example):

    server {
        listen 80;
    
        location /products/ {
            proxy_pass http://api_server;
            auth_jwt   "API";
            #...
        }
    }
    

    NGINX Plus can also obtain the JWT from a query string parameter. To configure this, include the token= parameter to the auth_jwt directive:

       #...
       auth_jwt          "API" token=$arg_apijwt;
       #...
    
  3. Specify the type of JWT - signed (JWS) or encrypted (JWE) - with the auth_jwt_type directive. The default value of the directive is “signed”, so for JWS, the directive can be omitted.

    server {
        listen 80;
    
        location /products/ {
            proxy_pass        http://api_server;
            auth_jwt          "API";
            auth_jwt_type     encrypted;
            #...
        }
    }
    
  4. Specify the path to the JSON Web Key file that will be used to verify JWT signature or decrypt JWT content, depending on what you are using. This can be done with the auth_jwt_key_file directive:

    server {
        listen 80;
    
        location /products/ {
            proxy_pass        http://api_server;
            auth_jwt          "API";
            auth_jwt_type     encrypted;
            auth_jwt_key_file conf/key.jwk;
        }
    }
    

How NGINX Plus Validates a JWT

A JWT is considered to be valid when the following conditions are met:

  • The signature can be verified (for JWS) or payload can be decrypted (for JWE) with the key found in the auth_jwt_key_file or auth_jwt_key_request (matching on the kid (“key ID”), if present, and alg (“algorithm”) header fields).
  • The JWT is presented inside the validity period, when defined by one or both of the nbf (“not before”) and exp (“expires”) claims.

Creating a JSON Web Key File

In order to validate the signature with a key or to decrypr data, a JSON Web Key (key.jwk) should be created. The file format is defined by JSON Web Key specification:

{"keys":
    [{
        "k":"ZmFudGFzdGljand0",
        "kty":"oct",
        "kid":"0001"
    }]
}

where:

  • the k field is the generated symmetric key (base64url-encoded) basing on a secret (fantasticjwt in the example). The secret can be generated with the following command:
$ echo -n fantasticjwt | base64 | tr '+/' '-_' | tr -d '='
ZmFudGFzdGljand0
  • the kty field defines the key type as a symmetric key (octet sequence)
  • the kid (Key ID) field defines a serial number for this JSON Web Key

Getting JWKs from Subrequest

NGINX Plus can be configured to fetch JSON Web Keys from the remote location - usually an identity provider, especially when using OpenID Connect. The IdP URI where the subrequest will be sent to is configured with the auth_jwt_key_request directive:

http {
    #...

    server {
        listen 80;
            #...

        location / {
            auth_jwt "closed site";
            auth_jwt_key_request /_jwks_uri; # Keys will be fetched by subrequest

            proxy_pass http://my_backend;
        }
    }
}

The URI may refer to an internal location (_jwks_uri) so that the JSON Web Key Set can be cached (proxy_cache and proxy_cache_path directives) to avoid validation overhead:

http {
    proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:1m max_size=10m;
    #...

    server {
        listen 80;
            #...

        location = /_jwks_uri {
            internal;
            proxy_method GET;
            proxy_cache  jwk; # Cache responses
            proxy_pass   https://idp.example.com/oauth2/keys; # Obtain keys from here
        }
    }
}

The full example of getting JWKs from a subrequest:

#
proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:1m max_size=10m;

server {
    listen 80; # Use SSL/TLS in production

    location / {
        auth_jwt             "closed site";
        auth_jwt_key_request /_jwks_uri;    # Keys will be fetched by subrequest

        proxy_pass http://my_backend;
    }

    location = /_jwks_uri {
        internal;
        proxy_method GET;
        proxy_cache  jwk; # Cache responses
        proxy_pass   https://idp.example.com/oauth2/keys; # Obtain keys from here
    }
}

See Also