Code Monkey home page Code Monkey logo

ngx-http-auth-jwt-module's Introduction

Auth-JWT NGINX Module

This is an NGINX module to check for a valid JWT and proxy to an upstream server or redirect to a login page. It supports additional features such as extracting claims from the JWT and placing them on the request/response headers.

Breaking Changes with v2

The v2 branch, which has now been merged to master includes breaking changes. Please see the initial v2 release for details,

Dependencies

This module depends on the JWT C Library. Transitively, that library depends on a JSON Parser called Jansson as well as the OpenSSL library.

Directives

This module requires several new nginx.conf directives, which can be specified at the http, server, or location levels.

Directive Description
auth_jwt_key The key to use to decode/verify the JWT, in binhex format -- see below.
auth_jwt_redirect Set to "on" to redirect to auth_jwt_loginurl if authentication fails.
auth_jwt_loginurl The URL to redirect to if auth_jwt_redirect is enabled and authentication fails.
auth_jwt_enabled Set to "on" to enable JWT checking.
auth_jwt_algorithm The algorithm to use. One of: HS256, HS384, HS512, RS256, RS384, RS512
auth_jwt_location Indicates where the JWT is located in the request -- see below.
auth_jwt_validate_sub Set to "on" to validate the sub claim (e.g. user id) in the JWT.
auth_jwt_extract_request_claims Set to a space-delimited list of claims to extract from the JWT and set as request headers. These will be accessible via e.g: $http_jwt_sub
auth_jwt_extract_response_claims Set to a space-delimited list of claims to extract from the JWT and set as response headers. These will be accessible via e.g: $sent_http_jwt_sub
auth_jwt_use_keyfile Set to "on" to read the key from a file rather than from the auth_jwt_key directive.
auth_jwt_keyfile_path Set to the path from which the key should be read when auth_jwt_use_keyfile is enabled.

Algorithms

The default algorithm is HS256, for symmetric key validation. When using one of the HS* algorithms, the value for auth_jwt_key should be specified in binhex format. It is recommended to use at least 256 bits of data (32 pairs of hex characters or 64 characters in total). Note that using more than 512 bits will not increase the security. For key guidelines please see NIST Special Publication 800-107 Recommendation for Applications Using Approved Hash Algorithms, Section 5.3.2 The HMAC Key.

To generate a 256-bit key (32 pairs of hex characters; 64 characters in total):

openssl rand -hex 32

Additional Supported Algorithms

The configuration also supports RSA public key validation via (e.g.) auth_jwt_algorithm RS256. When using the RS* alhorithms, the auth_jwt_key field must be set to your public key OR auth_jwt_use_keyfile should be set to on and auth_jwt_keyfile_path should point to the public key on disk. NGINX won't start if auth_jwt_use_keyfile is set to on and a key file is not provided.

When using an RS* algorithm with an inline key, be sure to set auth_jwt_key to the public key, rather than a PEM certificate. E.g.:

auth_jwt_key "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0aPPpS7ufs0bGbW9+OFQ
RvJwb58fhi2BuHMd7Ys6m8D1jHW/AhDYrYVZtUnA60lxwSJ/ZKreYOQMlNyZfdqA
rhYyyUkedDn8e0WsDvH+ocY0cMcxCCN5jItCwhIbIkTO6WEGrDgWTY57UfWDqbMZ
4lMn42f77OKFoxsOA6CVvpsvrprBPIRPa25H2bJHODHEtDr/H519Y681/eCyeQE/
1ibKL2cMN49O7nRAAaUNoFcO89Uc+GKofcad1TTwtTIwmSMbCLVkzGeExBCrBTQo
wO6AxLijfWV/JnVxNMUiobiKGc/PP6T5PI70Uv67Y4FzzWTuhqmREb3/BlcbPwtM
oQIDAQAB
-----END PUBLIC KEY-----";

When using an RS* algorithm with a public key file, do as follows:

auth_jwt_use_keyfile on;
auth_jwt_keyfile_path "/path/to/pub_key.pem";

A typical use case would be to specify the key and login URL at the http level, and then only turn JWT authentication on for the locations which you want to secure (or vice-versa). Unauthorized requests will result in a 302 Moved Temporarily response with the Location header set to the URL specified in the auth_jwt_loginurl directive, and a querystring parameter return_url whose value is the current / attempted URL.

If you prefer to return 401 Unauthorized rather than redirect, you may turn auth_jwt_redirect off:

auth_jwt_redirect off;

JWT Locations

By default, theAuthorization header is used to provide a JWT for validation. However, you may use the auth_jwt_location directive to specify the name of the header or cookie which provides the JWT:

auth_jwt_location HEADER=auth-token;  # get the JWT from the "auth-token" header
auth_jwt_location COOKIE=auth-token;  # get the JWT from the "auth-token" cookie

sub Validation

Optionally, the module can validate that a sub claim (e.g. the user's id) exists in the JWT. You may enable this feature as follows:

auth_jwt_validate_sub on;

Extracting Claims from the JWT

You may specify claims to be extracted from the JWT and placed on the request and/or response headers. This is especially handly because the claims will then also be available as NGINX variables.

If you only wish to access a claim as an NGINX variable, you should use auth_jwt_extract_request_claims so that the claim does not end up being sent to the client as a response header. However, if you do want the claim to be sent to the client in the response, then use auth_jwt_extract_response_claims instead.

Please note that number, boolean, array, and object claims are not supported at this time -- only string claims are supported. An error will be thrown if you attempt to extract a non-string claim.

Using Request Claims

For example, you could configure an NGINX location which redirects to the current user's profile. Suppose sub=abc-123, the configuration below would redirect to /profile/abc-123.

location /profile/me {
    auth_jwt_extract_request_claims sub;

    return 301 /profile/$http_jwt_sub;
}

Using Response Claims

Response claims are used in the same way, with the only differences being:

  • the variables are accessed via the $sent_http_jwt_* pattern, e.g. $sent_http_jwt_sub, and
  • the headers are sent to the client.

Extracting Multiple Claims

You may extract multiple claims by specifying all claims as arguments to a single directive, or by supplying multiple directives. The following two examples are equivalent.

auth_jwt_extract_request_claims sub firstName lastName;
auth_jwt_extract_request_claims sub;
auth_jwt_extract_request_claims firstName;
auth_jwt_extract_request_claims lastName;

Versioning

This module has historically not been versioned, however, we are now starting to version the module in order to add clarity. We will add releases here in GitHub with additional details. In the future we may also publish pre-built modules for a selection of NGINX versions.

Contributing

If you'd like to contribute to this repository, please first initiate the Git hooks by running ./.bin/init (note the . before bin) -- this will ensure that tests are run before you push your changes.

Environment Set-up for Visual Studio Code

  1. Install the C/C++ extension from Microsoft.
  2. Add a C/C++ config file at .vscode/c_cpp_properties.json with the following (or similar) content:
{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "~/Projects/nginx/objs/**",
                "~/Projects/nginx/src/**",
                "~/Projects/libjwt/include/**",
                "~/Projects/jansson/src/**"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c17",
            "cppStandard": "c++14",
            "intelliSenseMode": "linux-clang-x64"
        }
    ],
    "version": 4
}

Note the includePath additions above -- please update them as appropriate. Next we need to pull these sources.

Building NGINX

  1. Download the NGINX release matching the version you're targeting.
  2. Extract the NGINX archive to wherever you'd like.
  3. Update the includePath entires shown above to match the location you chose.
  4. Enter the directory where you extracted NGINX and run: ./configure --with-compat

Cloning libjwt

  1. Clone this repository as follows (replace <target_dir>): git clone [email protected]:benmcollins/libjwt.git <target_dir>
  2. Enter the directory and switch to the latest tag: git checkout $(git tag | sort -Vr | head -n 1)
  3. Update the includePath entires shown above to match the location you chose.

Cloning libjansson

  1. Clone this repository as follows (replace <target_dir>): git clone [email protected]:akheron/jansson.git <target_dir>
  2. Enter the directory and switch to the latest tag: git checkout $(git tag | sort -Vr | head -n 1)
  3. Update the includePath entires shown above to match the location you chose.

Verifying Compliation

Once you save your changes to .vscode/c_cpp_properties.json, you should see that warnings and errors in the Problems panel go away, at least temprorarily. Hopfeully they don't come back, but if they do, make sure your include paths are set correctly.

Building and Testing

The ./scripts.sh file contains multiple commands to make things easy:

Command Description
build_module Builds the NGINX image.
rebuild_module Re-builds the NGINX image.
start_nginx Starts the NGINX container.
stop_nginx Stops the NGINX container.
cp_bin Copies the compiled binaries out of the NGINX container.
build_test_runner Builds the images used by the test stack (uses Docker compose).
rebuild_test_runner Re-builds the images used by the test stack.
test Runs test.sh against the NGINX container (uses Docker compose).
test_now Runs test.sh without rebuilding.

You can run multiple commands in sequence by separating them with a space, e.g.:

./scripts.sh build_module test

To build the Docker images, module, start NGINX, and run the tests against, you can simply do:

./scripts.sh all

When you make a change to the module run ./scripts.sh build_module test to build a fresh module and run the tests. Note that rebuild_module is not often needed as build_module hashes the module's source files which will cause a cache miss while building the container, causing the module to be rebuilt.

When you make a change to the test NGINX config or test.sh, run ./scripts.sh test to run the tests. Similar to above, the test sources are hashed and the containers will be rebuilt as needed.

The image produced with ./scripts.sh build_module only differs from the official NGINX image in two ways:

  • the JWT module itself, and
  • the nginx.conf file is overwritten with our own.

The tests use a customized NGINX image, distinct from the main image, as well as a test runner image. By running ./scripts.sh test, the two test containers will be stood up via Docker compose, then they'll be started, and the tests will run. At the end of the test run, both containers will be automatically stopped and destroyed. See below to learn how to trace test failures across runs.

Tracing Test Failures

After making changes and finding that some tests fail, it can be difficult to understand why. By default, logs are written to Docker's internal log mechanism, but they won't be persisted after the test run completes and the containers are removed.

If you'd like to persist logs across test runs, you can configure the log driver to use journald (on Linux/Unix systems for example). You can do this by setting the environment variable LOG_DRIVER before running the tests:

# need to rebuild the test runner with the proper log driver
LOG_DRIVER=journald ./scripts.sh rebuild_test_runner

# run the tests
./scripts.sh test

# check the logs
journalctl -eu docker CONTAINER_NAME=jwt-nginx-test

Now you'll be able to see logs from previous test runs. The best way to make use of this is to open two terminals, one where you run the tests, and one where you follow the logs:

# terminal 1
./scripts.sh test

# terminal 2
journalctl -fu docker CONTAINER_NAME=jwt-nginx-test

ngx-http-auth-jwt-module's People

Contributors

fitzyjoe avatar hvt avatar joshmccullough avatar kevinmichaelchen avatar knownentity avatar lewisemm avatar max-lt avatar maxx-t avatar orgads avatar penumbra23 avatar smartarray avatar swaeberle avatar timunderhay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ngx-http-auth-jwt-module's Issues

How t use this module with Django REST framework?

I'm working on a VOD platform in terms of a white-label product.
Most stuff is already done but for the moment I'm not able to solve one problem, how to deliver content in HLS format to the client securely? And with secure, I mean authorization.

All HLS content is stored on a S3 storage (non-AWS) and all m3u8 playlist and all segments must be available to the client as soon as the player (VideoJS) requests an individual segment referenced by a m3u8 playlist. In other words, this means that the Bucket itself must run as public from a bucket policy perspective, otherwise VideoJS will fail to load segments.

My Idea now is that I place NGINX or OpenResty in front of the S3 Storage to handle
authorization of a client request. I have never done something like this, so I would like to get some useful references before I start implementing.

The VOD service itself is basically a combination of a Backend, Django API using DRF, and a Frontend App (AngularJS). The Backend uses JWT to authenticate clients. So it would be awesome if I can simply create some kind of token I can pass to the client, maybe another JWT or the same JWT the Client gets after log in at the frontend.

I'm not sure what exactly I need here, but it seems that I need to make NGINX/OpenResty open a subrequest as soon as a client wants data from the S3 storage if I understood the docs right.

At my Django backend, I have the following 3 endpoints:

re_path(r'^api/v1/token/obtain$', obtain_jwt_token),       # Returns a new Token
re_path(r'^api/v1/token/refresh$', refresh_jwt_token),     # Returns a new Token in exchange for an old one
re_path(r'^api/v1/token/verify$', verify_jwt_token),       # Returns the Token if Valid else Bad Request

What do I need to do in order to make DRF and NGINX marriage?
What kind of key does NGINX expect to validate the JWT?

please also see:

https://jpadilla.github.io/django-rest-framework-jwt/

Thanks in advance, and sorry for the long reading.

set the value of auth_jwt_key implicitely

I try to use this module. when I explicitly set the value for auth_jwt_key it works just fine. But if I set it as: auth_jwt_key $public_key; which is set by a js module beforehand, I get the error: nginx: [error] failed to open public key file
is there a way to solve this issue?

very basic misunderstanding...

Hello,

I followed the post post https://mostafa-asg.github.io/post/nginx-authentication-with-jwt/
I think it went successful, but...

I got stuck on the last stage. I think I make just some noobie mistake, but I keep receiving: 401 Authorisation required. Giving an example location:

location /test/{
auth_jwt_enabled on;
auth_jwt_algorithm HS256;
auth_jwt_validate_email off;
auth_jwt_key "secret";
try_files /test.html /test.html;
}

I assume that to get to the www.mysite.com/test/ I need to add header with JWT. So I go to jwt.io, generate the token using "secret" as the secret key to get JWT (like https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o).

So I authorise by adding the header and calling:

curl -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o" https://www.mysite.com/test/

If you give me a hint what I am doing wrong, I would be very grateful.

Thank you very much!

Best regards
Tomasz Wojtas

RSA Key Support

Am I correct in stating that this module currently supports only symmetric keys, but not asymmetric RSA private key-signed tokens? If so, I would like to make a feature request to support tokens signed by RSA keys.

I'm specifically looking for RS256.

Thank you!

Loading RS256 key from file

Hi!

First off, nice plugin, saves a lot of trouble! 👏

We deploy this image on our K8s cluster using Helm charts, where the PEM encoded key is a generated on deployment and is a K8s secret. Helm can't work with keys generated on install and paste the content in the Nginx configuration (because it's being generated in the same operation). One solution would be to map the secret onto a file and use that path as a mounted volume.

Would reading the PEM file from a file be a new feature in this plugin?

I've already forked the repo and added two attributes that load the key, it serves us well. If it's cool for you I can make a PR to include it officially?

Remove X- header prefix

Custom proprietary headers can be added using the 'X-' prefix, but this convention was deprecated in June 2012, because of the inconveniences it caused when non-standard fields became standard in RFC-6648

Proposal

Add auth_jwt_header_prefix to allow users to specify custom header prefix.

e.g.,

auth_jwt_header_prefix "clarakm"

Then x-email and x-userid would be renamed to clarakm-email and clarakm-userid.

Leveraging JWT Claims for Logging purpose

Hi,

This may be a newbie question but does this module support JWT Claim retrieval from incoming request header as mentionned here ? : https://www.nginx.com/blog/authenticating-api-clients-jwt-nginx-plus/#auth_jwt
You can see on the link above that some variables (such as $jwt_header_xxxx and $jwt_claim_xxxx) representing JWT header and payload fields are available and can be sent to the forwarded API server for request processing per user.
The readme doesn't mention such variables.
More generally, does this module offer the same functionalities as the one coming natively with NGINX Plus which is called "ngx_http_auth_jwt_module".

Thanks in advance for your help

Failed to turn hex key into binary

I'm trying to using the Dockerfile to run nginx

My configuration is

        auth_jwt_enabled on;
        auth_jwt_key superSecretSharedSigningKey; 
        auth_jwt_algorithm HS256;    #  using HMAC for signing 
        auth_jwt_validate_email off;

I'm using the jwt builder from: http://jwtbuilder.jamiekurtz.com/

On request I'm getting the below error

[error] 7#0: *6 failed to turn hex key into binary, client: 10.255.0.2, server: , request: "GET /api/platform/tool/userauth/user/add HTTP/1.1", host: "localhost:8080"

Support extraction of non-string claims

As indicated in the README:

Please note that number, boolean, array, and object claims are not supported at this time -- only string claims are supported. An error will be thrown if you attempt to extract a non-string claim.

We should support at least number and boolean claims. Supporting object and array claims is more difficult, but we should support extracting a claim from an array or object e.g. my_claim_array[0] or my_claim_obj[some_key][some_other_key].

Make `sub` claim optional (and perhaps others?)

Hi guys, I am using your module and I'm wonder if we can made the sub claim optional?

This is the output in nginx error log when sending our JWT token:

2021/11/26 06:44:30 [error] 9#9: *14 the jwt does not contain a subject, client: 8.8.8.8, server: , request: "GET /secure-auth-header/ HTTP/1.1", host: "3.3.3.3:8000"

Example for common usage with ingress-nginx

Hi,
since this module is solving an crucial gap specifically for the nginx community I was thinking it might make sense to add a simplistic example configuration somewhere how to configure it open source kubernetes community projects like for
example https://github.com/kubernetes/ingress-nginx

Maybe with snippet:

nginx.ingress.kubernetes.io/configuration-snippet: |

or:
https://kubernetes.github.io/ingress-nginx/examples/customization/custom-configuration/ or differently?

I am a total beginner here.

If I would try to configure INGRESS-NGINX today I would be kind of lost how to start.

My IDP exposes

"jwks_uri": "https://<IDP>/oauth2/certs",

uri content:

{
  "keys": [
    {
      "kty": "RSA",
      "x5t#S256": "xxxxxx",
      "e": "AQAB",
      "use": "sig",
      "kid": "xxxxxxx",
      "x5c": [
        "xxxxxxxxxxxxxx"
      ],
      "alg": "RS256",
      "n": "xxxxxxxxxxxxxx"
    }
  ]
}

Get JWT from Authorization header

In our project we submit the JWT in the Authorization header in the format: 'Bearer <token>'. We do not use any cookies.

We would like to use your module in our project, but I see that in e42d2a0 you removed the Authorization header check. Do you have any plans to support getting the token from the Authorization header or would you accept a pull request which adds this feature?

libjwt 1.9.0?

I've noticed that the Dockerfile uses libjwt 1.8.0, so I've taken that to mean that that's what's officially supported. Would any work be needed to support 1.9.0?

I asked because I've undertaken the work to implement this in MacPorts (still waiting on PR macports/macports-ports#1925 to be merged) but this also meant adding libjwt to MacPorts (macports/macports-ports#1924). Because of the whole version 1.8.0 thing, that's what I implemented in MacPorts, but their team is pressing me to implement the latest. And because MacPorts doesn't implement versioned dependencies, I'm resisting until this project blesses libjwt 1.9.0.

Cheers, gents.

Error compile in Ubuntu 22.04

Hello there,

When trying to install nginx 1.22 with ngx-http-auth-jwt-module...

./configure --prefix=/var/www/html --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --add-dynamic-module=../ngx-http-auth-jwt-module --with-http_ssl_module --with-http_image_filter_module=dynamic --modules-path=/etc/nginx/modules --with-http_v2_module --with-stream=dynamic --with-http_addition_module --with-http_mp4_module

make modules

../ngx-http-auth-jwt-module/src/ngx_http_auth_jwt_module.c
../ngx-http-auth-jwt-module/src/ngx_http_auth_jwt_module.c: In function ‘loadAuthKey’:
../ngx-http-auth-jwt-module/src/ngx_http_auth_jwt_module.c:446:2: error: ignoring return value of ‘fread’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
446 | fread(conf->_auth_jwt_keyfile.data, 1, keySize, keyFile);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [objs/Makefile:1407: objs/addon/src/ngx_http_auth_jwt_module.o] Error 1

Project license

Hi,
Thanks for sharing! I don't see the open source license terms specified anywhere. Which license are you using for this project?

Makefile

A Makefile might help productivity, so you don't have to grep history or go searching for past commands you've run.

You'd run make stop remove build to stop the running containers, remove the images, and build and test.

.PHONY: stop
    docker stop $(shell docker ps -aqf "name=jwt-nginx-cont")

.PHONY: remove
    @$(MAKE) stop
    docker rmi -f $(shell docker ps -aqf "name=jwt-nginx")

.PHONY: build
    ./build.sh

Note: Makefiles don't like spaces, so you gotta convert them to tabs.

failed to parse JWT, error code 22

Hi, I am trying to implement HS256 based validation. But Iam getting 401 and
failed to parse JWT, error code 22, client: 192.168.1.10, server: , request: "GET /api/1 HTTP/1.1", host: "192.168.1.17" in error logs.

My conf looks like this

server {
    listen       80;
        error_log /var/log/nginx/error-1.log debug;
        access_log /var/log/nginx/access-1.log main_ext;
        auth_jwt_enabled on;
        auth_jwt_key "jXnZr4u7x!A%D*G-KaPdSgVkYp3s5v8y";
        auth_jwt_algorithm HS256;

location / {
    proxy_pass http://localhost:808/dev;
}}

Any help would be really appreciated.

1.24 module doesn't 'seem' to be compiled against nginx 1.24.0

Hi ..

I downloaded the prebuilt:
https://github.com/TeslaGov/ngx-http-auth-jwt-module/releases/download/2.0.1/ngx_http_auth_jwt_module_2.0.1_nginx_1.24.0.tgz

and tried to use this with nginx 1.24
from https://nginx.org/packages/debian/pool/nginx/n/nginx/nginx_1.24.0-1~bullseye_amd64.deb

when trying to load the module .. I get:

nginx: [emerg] module "/etc/nginx/modules/ngx_http_auth_jwt_module.so" version 1022001 instead of 1024000 in /etc/nginx/nginx.conf:5

image

Invalid "Authorization" header may lead to segmentation fault

Invalid "Authorization" header may lead to segmentation fault:

2018/06/06 22:03:26 [emerg] 8#8: *10 malloc(18446744073709551611) failed (12: Out of memory), client: 172.17.0.1, server: localhost, request: "GET /secure-auth-header/ HTTP/1.1", host: "localhost:8000"
2018/06/06 22:03:27 [notice] 1#1: signal 17 (SIGCHLD) received from 8
2018/06/06 22:03:27 [alert] 1#1: worker process 8 exited on signal 11 (core dumped)
2018/06/06 22:03:27 [notice] 1#1: start worker process 9

How to reproduce:

  • In your nginx.conf file: change error_log /var/log/nginx/error.log info to error_log /dev/stderr info (you wont see anything otherwise).

  • Add this test:

TEST_INVALID_AUTH_HEADER=`curl -X GET -o /dev/null --silent --head --write-out '%{http_code}\n' http://${MACHINE_IP}:8000/secure-auth-header/index.html -H 'cache-control: no-cache' --header "Authorization: X"`
if [ "TEST_INVALID_AUTH_HEADER" -eq "302" ];then
  echo -e "${GREEN}Secure test with jwt auth header pass ${TEST_INVALID_AUTH_HEADER}${NONE}";
else
  echo -e "${RED}Secure test with jwt auth header fail ${TEST_INVALID_AUTH_HEADER}${NONE}";
fi

Cause:

Line 422:
authorizationHeaderStr.len = authorizationHeader->value.len - (sizeof("Bearer ") - 1);
-> "len" can be negative and it will fail in the ngx_str_t_to_char_ptr function on memory allocation.

Additional JWT algorithm support

It would be great to support additional algorithms as changing them out in production can be difficult if you have a lot of consumers of the public keys.

Specifically supporting the ones from this list: HS384, HS512, RS384, RS512, ES256, ES384 and ES512

Get JWT from other HTTP headers

As far as I can see from the code, you can only load the JWT from either the Authorization header or from a cookie. Would be nice if you could also configure this module to load it from another header like the Proxy-Authorization header for example.

Blacklist

I need to be able to blacklist user accounts. This is almost never used, but it is a security requirement for some projects. This could be as simple as a delimited file of "sub" claims that should be denied.

Passing variable to auth_jwt_key does not work

I'm trying to combine two parts to make the JWT key:
set $key "${firstPart}SecoundPart";
auth_jwt_key $key;

Not too sure if its the module that needs to interpolation the ${variable}, or the module is not able to use the referanse to $key. Or perhaps I completely missed something else.

Scenario:: I need ${firstPart} to actually be the regex'ed out part/folder(in binHex) of the url, and combine it with a secret key to use nginx to verify if client with JWT can access files in a folder. As then backend can generate JWT (with expiration of 1day) for that folder, thus giving front-end access to all needed files, without having to check complex user->group->project->file auth every time.

Thanks for making a great module!

may ba a crash

Hi, TeslaGov, I use it in a project, It works well. thanks for your awesome jwt module.
But I found a problem while testing by using the http header segment(empty jwt string):
"Authorization: Bearer "

line: 429 authorizationHeaderStr.len = authorizationHeader->value.len - (sizeof("Bearer ") - 1);
authorizationHeaderStr.len may be nagative and would lead to a crash

suggest fix:
ngx-http-auth-jwt-module.c line 424
if (authorizationHeader != NULL) -> if (authorizationHeader != NULL && authorizationHeader->value.len > 1)

Best practice to validate multiple JWT

Hi

What is the best practice to validate multiple JWT?

I've a lot of users and each user have a personal JWT; each user can access to one o more 'location'/'route'.
Should I create, for example, a test-jwt-nginx.conf for each user?

Thank you,
Valentino

Version Tagged Releases

I'd like to request the addition of version tags for module releases. The reason is I'd like to add this module to MacPorts but the portfiles require file hashes for each source file, so they must be calculated against a specific version.

master_sites https://github.com/TeslaGov/ngx-http-auth-jwt-module/archive/:jwt_module
...
...

variant jwt description {Add JWT (Javascript Web Token) support to server} {
    set jwt_filename     ngx-http-auth-jwt-module
    set jwt_version      master
    set jwt_distname     ${jwt_filename}-${jwt_version}
    distfiles-append        ${jwt_version}.tar.gz:jwt_module
    # curl -OL https://github.com/KensingtonTech/ngx-http-auth-jwt-module/archive/master.tar.gz && shasum -a256 master.tar.gz && openssl rmd160 master.tar.gz
    checksums-append        ${jwt_version}.tar.gz \
                            rmd160  6c4e41f34ed807ce0df345582f6d3efc940bcb05 \
                            sha256  b91c081a16873264f3a0175ca65786197b2bb8fb824bce3e0f509800075ef939
    configure.args-append   --add-dynamic-module=${workpath}/${jwt_distname}
    depends_lib-append      port:libjwt
}

Without a tag, it will always fetch the latest master from the GitGub archive (i.e. https://github.com/TeslaGov/ngx-http-auth-jwt-module/archive/master.tar.gz ), which can change at any time and thus break MacPorts because the file hashes won't match. By adding a version tag, I can pull specific versions from the GitHub archive, like https://github.com/TeslaGov/ngx-http-auth-jwt-module/archive/v1.0.tar.gz .

Thank you!

Is it possible to compile with -fPIE?

Sorry for a newbie question but I am not too good with compilers.
I tried to compile your project on Ubuntu 16.04. To be "binary compatible" the module must be compiled with the same flags, so I've taken these from Ubuntu nginx:

--with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now'

The compilations fails with:

/usr/bin/ld: objs/addon/src/ngx_http_auth_jwt_module.o: relocation R_X86_64_PC32 against undefined symbol `ngx_http_core_module' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value

As far as I figure this is related to those "position-independent executable" flags (-fPIE, -pie) used by Ubuntu. So my question is if it's even possible to compile your module with such flags?

Unable to compile since PR #56

Since PR #56, I'm not able to compile Nginx with this module.

I got this compilation error:

cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g   -I src/core -I src/event -I src/event/modules -I src/os/unix -I objs -I src/http -I src/http/modules \
	-o objs/addon/src/ngx_http_auth_jwt_module.o \
	../ngx-http-auth-jwt-module/src/ngx_http_auth_jwt_module.c
../ngx-http-auth-jwt-module/src/ngx_http_auth_jwt_module.c: In function ‘loadAuthKey’:
../ngx-http-auth-jwt-module/src/ngx_http_auth_jwt_module.c:438:2: error: ignoring return value of ‘fread’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
  438 |  fread(conf->_auth_jwt_keyfile.data, 1, keySize, keyFile);
      |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [objs/Makefile:1250: objs/addon/src/ngx_http_auth_jwt_module.o] Error 1
make: *** [Makefile:10: build] Error 2

I tried different Nginx versions without success, which leads me to try the previous commit of this module which was a success.

Build issue under ubuntu

Trying to make a docker container in ubuntu, I get:

/usr/bin/ld: objs/addon/src/ngx_http_auth_jwt_module.o: relocation R_X86_64_PC32 against undefined symbol `ngx_http_core_module' can not be used when making a shared object; recompile with -fPIC

Any idea why?

Dockerize the build

Add Dockerfile, preferably based on Alpine Linux (5MB), that can build this module.

Proposal: Remove the auth_jwt_redirect directive

The auth_jwt_redirect directive makes the code overly complicated and can be easily replaced by the nginx's error_page directive:

from:

location ~ ^/secure/ {
    auth_jwt_enabled on;
    auth_jwt_validation_type COOKIE=rampartjwt;
    auth_jwt_redirect on;
    auth_jwt_loginurl "https://teslagov.com";
}

to:

location @login_err_redirect {
    return 302 https://teslagov.com?redirect=$request_uri&$args;
}

location ~ ^/secure/ {
    auth_jwt_enabled on;
    auth_jwt_validation_type COOKIE=rampartjwt;
    error_page 401 = @login_err_redirect;
}

I'm maintaining a branch that had this simplification (around 130 less lines (~25%) for the module.c file, and no more gotos (#13), diff here) and can make a PR if you are interested.

Refresh token

I would like to support shorter life for tokens. Typically, this is done with 2 tokens. A refresh-token that is used to obtain a short lived bearer token. However, I'd like to keep it simple for my partner apps that are under the umbrella of this single sign on.

I'm considering adding a feature to issue a new token as a Set-Cookie for requests that

  • contain a valid token
  • contain a token that is older than some short period (like a minute - to prevent issuing on every request)
  • contains an authenticated-on JWT claim that has happened within a period (like a day - to prevent chaining these tokens forever)

Missing sub or emailAddress leads to segmentation fault

Missing sub or emailAddress leads to segmentation fault

When checking a token that have no "emailAddress" or/and "sub" field, nginx worker process get killed for segfault.

without emailAddress:

 [error] 12#0: *9 auth_jwt_validation_type.len 17, client: 172.17.0.1, server: localhost, request: "GET /secure/index.html HTTP/1.1", host: "127.0.0.1:8000"
 [error] 12#0: *9 the jwt does not contain an email address, client: 172.17.0.1, server: localhost, request: "GET /secure/index.html HTTP/1.1", host: "127.0.0.1:8000"
 [notice] 1#0: signal 17 (SIGCHLD) received
 [alert] 1#0: worker process 12 exited on signal 11 (core dumped)
 [notice] 1#0: start worker process 13

without sub:

 [error] 10#0: *7 auth_jwt_validation_type.len 17, client: 172.17.0.1, server: localhost, request: "GET /secure/index.html HTTP/1.1", host: "127.0.0.1:8000"
 [error] 10#0: *7 the jwt does not contain a subject, client: 172.17.0.1, server: localhost, request: "GET /secure/index.html HTTP/1.1", host: "127.0.0.1:8000"
 [notice] 1#0: signal 17 (SIGCHLD) received
 [alert] 1#0: worker process 10 exited on signal 11 (core dumped)
 [notice] 1#0: start worker process 11

Workaround:

Adding goto redirect; at the end of the two blocks that make these verifications does the trick BUT as we face a JWT that have been correctly signed but does not meets requirements I suggest not to redirect to the authentication server (if redirect set) to avoid redirection loop nor returning with a 401 status (because it could be handled with the nginx's error_page directive (like error_page 401 = /go_to_login;) and lead to a redirection loop too.
Adding a else block with the custom headers affectation works as well.. but headers will be unset.

For now, i'm returning a 403 (NGX_HTTP_FORBIDDEN) code because a 403 response should prevent the request to be repeated (https://tools.ietf.org/html/rfc2616#section-10.4.2).

How to reproduce:

In your nginx.conf file: change your error_log to error_log /dev/stderr info; (you wont see anything otherwise).

Run tests with:

# Missing "sub"
VALIDJWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaXJzdE5hbWUiOiJoZWxsbyIsImxhc3ROYW1lIjoid29ybGQiLCJlbWFpbEFkZHJlc3MiOiJoZWxsb3dvcmxkQGV4YW1wbGUuY29tIiwicm9sZXMiOlsidGhpcyIsInRoYXQiLCJ0aGVvdGhlciJdLCJpc3MiOiJpc3N1ZXIiLCJwZXJzb25JZCI6Ijc1YmIzY2M3LWI5MzMtNDRmMC05M2M2LTE0N2IwODJmYWRiNSIsImV4cCI6MTkwODgzNTIwMCwiaWF0IjoxNDg4ODE5NjAwLCJ1c2VybmFtZSI6ImhlbGxvLndvcmxkIn0.lD6jUsazVtzeGhRTNeP_b2Zs6O798V2FQql11QOEI1Q
# Missing "email"
VALIDJWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOTMzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAsImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.tJoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM

Question about the Docker Build ? Throws and error in some cases.

I'll have to check the Docker Engine and Docker Compose version, but I tried to build the module on a server using the syntax in your sample Docker file. On on my installation it threw an error regarding "BUILD_FLAGS=''". I changed the syntax of that and it built successfully, but it builds fine with the syntax shown below on other systems. To 'fix' it on that other system I explicitly use v. 1.24.0 and basically get rid of the logic:

RUN ./configure --with-compat --add-dynamic-module=../ngx-http-auth-jwt-module --with-cc-opt=-DNGX_LINKED_LIST_COOKIES=1

and it seems to build and function OK.

I'll have to refactor my Docker File if I want to use the module though because I simply compile it now and then use it for another build of the same version of NGINX.

RUN <<`
BUILD_FLAGS=''
MAJ=$(echo ${NGINX_VERSION} | cut -f1 -d.)
MIN=$(echo ${NGINX_VERSION} | cut -f2 -d.)
REV=$(echo ${NGINX_VERSION} | cut -f3 -d.)

# NGINX 1.23.0+ changes cookies to use a linked list, and renames `cookies` to `cookie`
if [ "${MAJ}" -gt 1 ] || [ "${MAJ}" -eq 1 -a "${MIN}" -ge 23 ]; then
	BUILD_FLAGS="${BUILD_FLAGS} --with-cc-opt='-DNGX_LINKED_LIST_COOKIES=1'"
fi

./configure --with-compat --add-dynamic-module=../ngx-http-auth-jwt-module ${BUILD_FLAGS}
make modules
`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.