{"id":15195061,"url":"https://github.com/auth0/nginx-jwt","last_synced_at":"2025-10-02T10:31:40.807Z","repository":{"id":29667260,"uuid":"33209445","full_name":"auth0/nginx-jwt","owner":"auth0","description":"Lua script for Nginx that performs reverse proxy auth using JWT's","archived":true,"fork":false,"pushed_at":"2017-02-09T21:28:01.000Z","size":635,"stargazers_count":535,"open_issues_count":26,"forks_count":119,"subscribers_count":129,"default_branch":"master","last_synced_at":"2024-09-28T23:06:18.329Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/auth0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-31T20:56:16.000Z","updated_at":"2024-09-06T00:14:08.000Z","dependencies_parsed_at":"2022-09-03T21:20:43.124Z","dependency_job_id":null,"html_url":"https://github.com/auth0/nginx-jwt","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/auth0%2Fnginx-jwt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/auth0%2Fnginx-jwt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/auth0%2Fnginx-jwt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/auth0%2Fnginx-jwt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/auth0","download_url":"https://codeload.github.com/auth0/nginx-jwt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234974957,"owners_count":18916144,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-27T23:06:16.851Z","updated_at":"2025-10-02T10:31:35.533Z","avatar_url":"https://github.com/auth0.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# JWT Auth for Nginx\n\n**nginx-jwt** is a [Lua](http://www.lua.org/) script for the [Nginx](http://nginx.org/) server (running the [HttpLuaModule](http://wiki.nginx.org/HttpLuaModule)) that will allow you to use Nginx as a reverse proxy in front of your existing set of HTTP services and secure them (authentication/authorization) using a trusted [JSON Web Token (JWT)](http://jwt.io/) in the `Authorization` request header, having to make little or no changes to the backing services themselves.\n\n## Contents\n\n- [Key Features](#key-features)\n- [Install](#install)\n- [Configuration](#configuration)\n- [Usage](#usage)\n- [API Reference](#api-reference)\n- [Tests](#tests)\n- [Packaging](#packaging)\n- [Issue Reporting](#issue-reporting)\n- [Contributors](#contributors)\n- [License](#license)\n\n## Key Features\n\n* Secure an existing HTTP service (ex: REST API) using Nginx reverse-proxy and this script\n* Authenticate an HTTP request with the verified identity contained with in a JWT\n* Optionally, authorize the same request using helper functions for asserting required JWT claims\n\n## Install\n\n\u003e **IMPORTANT**: **nginx-jwt** is a Lua script that is designed to run on Nginx servers that have the [HttpLuaModule](http://wiki.nginx.org/HttpLuaModule) installed. But ultimately its dependencies require components available in the [OpenResty](http://openresty.org/) distribution of Nginx. Therefore, it is recommended that you use **OpenResty** as your Nginx server, and these instructions make that assumption.\n\nInstall steps:\n\n1. Download the latest archive package from [releases](https://github.com/auth0/nginx-jwt/releases).\n1. Extract the archive and deploy its contents to a directory on your Nginx server.\n1. Specify this directory's path using ngx_lua's [lua_package_path](https://github.com/openresty/lua-nginx-module#lua_package_path) directive:  \n    ```lua\n    # nginx.conf:\n\n    http {\n        lua_package_path \"/path/to/lua/scripts;;\";\n        ...\n    }\n    ```\n\n## Configuration\n\n\u003e At the moment, `nginx-jwt` only supports symmetric keys (`alg` = `hs256`), which is why you need to configure your server with the shared JWT secret below.\n\n1. Export the `JWT_SECRET` environment variable on the Nginx host, setting it equal to your JWT secret.  Then expose it to Nginx server:  \n    ```lua\n    # nginx.conf:\n\n    env JWT_SECRET;\n    ```\n1. If your JWT secret is Base64 (URL-safe) encoded, export the `JWT_SECRET_IS_BASE64_ENCODED` environment variable on the Nginx host, setting it equal to `true`.  Then expose it to Nginx server:  \n    ```lua\n    # nginx.conf:\n\n    env JWT_SECRET_IS_BASE64_ENCODED;\n    ```\n\n## Usage\n\nNow we can start using the script in reverse-proxy scenarios to secure our backing service.  This is done by using the [access_by_lua](https://github.com/openresty/lua-nginx-module#access_by_lua) directive to call the `nginx-jwt` script's [`auth()`](#auth) function before executing any [proxy_* directives](http://nginx.org/en/docs/http/ngx_http_proxy_module.html):\n\n```lua\n# nginx.conf:\n\nserver {\n    location /secure_this {\n        access_by_lua '\n            local jwt = require(\"nginx-jwt\")\n            jwt.auth()\n        ';\n\n        proxy_pass http://my-backend.com$uri;\n    }\n}\n```\n\nIf you attempt to cURL the above `/secure_this` endpoint, you're going to get a `401` response from Nginx since it requires a valid JWT to be passed:\n\n```bash\ncurl -i http://your-nginx-server/secure_this\n```\n\n```\nHTTP/1.1 401 Unauthorized\nServer: openresty/1.7.7.1\nDate: Sun, 03 May 2015 18:05:00 GMT\nContent-Type: text/html\nContent-Length: 200\nConnection: keep-alive\n\n\u003chtml\u003e\n\u003chead\u003e\u003ctitle\u003e401 Authorization Required\u003c/title\u003e\u003c/head\u003e\n\u003cbody bgcolor=\"white\"\u003e\n\u003ccenter\u003e\u003ch1\u003e401 Authorization Required\u003c/h1\u003e\u003c/center\u003e\n\u003chr\u003e\u003ccenter\u003eopenresty/1.7.7.1\u003c/center\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nTo create a valid JWT, we've included a handy tool that will generate one given a payload and a secret.  The payload must be in JSON format and at a minimum should contain a `sub` (subject) element.  The following command will generate a JWT with an arbitrary payload and the specific secret used by the proxy:\n\n```bash\ntest/sign '{\"sub\": \"flynn\"}' 'My JWT secret'\n```\n\n```\nPayload: { sub: 'flynn' }\nSecret: JWTs are the best!\nToken: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwZXRlIiwiaWF0IjoxNDMwNjc3NjYzfQ.Zt4qnQyljbqLvAN7BQSuu14z5PjKcPpZZY85hDFVN3E\n```\n\nYou can then use the above `Token` (the JWT) and call the Nginx server's `/secure_this` endpoint again:\n\n```bash\ncurl -i http://your-nginx-server/secure_this -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwZXRlIiwiaWF0IjoxNDMwNjc3NjYzfQ.Zt4qnQyljbqLvAN7BQSuu14z5PjKcPpZZY85hDFVN3E'\n```\n\n```\nHTTP/1.1 200 OK\nServer: openresty/1.7.7.1\nDate: Sun, 03 May 2015 18:34:18 GMT\nContent-Type: text/plain\nContent-Length: 47\nConnection: keep-alive\nX-Auth-UserId: flynn\nX-Powered-By: Express\nETag: W/\"2f-8fc49de2\"\n\nThe reverse-proxied response!\n```\n\nIn this case the Nginx server has authorized the caller and performed a reverse proxy call to the backing service's endpoint.  Notice too that the **nginx-jwt** script has tacked on an extra response header called `X-Auth-UserId` that contains the value passed in the JWT payload's subject.  This is just for convenience, but it does help verify that the server does indeed know who you are.\n\nThe `jwt.auth()` function used above can actually do a lot more. See the [API Reference](#api-reference) section for more details.\n\n## API Reference\n\n### auth\n\nSyntax: `auth([claim_specs])`\n\nAuthenticates the current request, requiring a JWT bearer token in the `Authorization` request header.  Verification uses the value set in the `JWT_SECRET` (and optionally `JWT_SECRET_IS_BASE64_ENCODED`) environment variables.\n\nIf authentication succeeds, then by default the current request is authorized by virtue of a valid user identity.  More specific authorization can be accomplished via the optional `claim_specs` parameter.  If provided, it must be a Lua [Table](http://www.lua.org/pil/2.5.html) where each key is the name of a desired claim and each value is a [pattern](http://www.lua.org/pil/20.2.html) that can be used to test the actual value of the claim.  If your claim value is more complex that what a pattern can handle, you can pass an anonymous function instead that has the signature `function (val)` and returns a truthy value (or just `true`) if `val` is a match.  You can also use the [`table_contains`](#table_contains) helper function to easily check for an existing value in an array table.\n\nFor example if we wanted to ensure that the JWT had an `aud` (Audience) claim value that started with `foo:` and a `roles` claim that contained a `marketing` role, then the `claim_specs` parameter might look like this:\n\n```lua\n# nginx.conf:\n\nserver {\n    location /secure {\n        access_by_lua '\n            local jwt = require(\"nginx-jwt\")\n            jwt.auth({\n                aud=\"^foo:\",\n                role=function (val) return jwt.table_contains(val, \"marketing\") end\n            })\n        ';\n\n        proxy_pass http://my-backend.com$uri;\n    }\n}\n```\nand if our JWT's payload of claims looked something like this, the above `auth()` call would succeed:\n\n```json\n{\n    \"aud\": \"foo:user\",\n    \"roles\": [ \"sales\", \"marketing\" ]\n}\n```\n\n**NOTE:** the **auth** function should be called within the [access_by_lua](https://github.com/openresty/lua-nginx-module#access_by_lua) or [access_by_lua_file](https://github.com/openresty/lua-nginx-module#access_by_lua_file) directive so that it can occur before the Nginx **content** [phase](http://wiki.nginx.org/Phases).\n\n### table_contains\n\nSyntax: `table_contains(table, item)`\n\nA helper function that checks to see if `table` (a Lua [Table](http://www.lua.org/pil/2.5.html)) contains the specified `item`.  If it does, the function returns `true`; otherwise `false`.  This is particularly helpful for checking for a value in an array:\n\n```lua\narray = { \"foo\", \"bar\" }\ntable_contains(array, \"foo\") --\u003e true\n```\n\n## Tests\n\nThe best way to develop and test the **nginx-jwt** script is to run it in a virtualized development environment.  This allows you to run Ngnix separate from your host machine (i.e. your Mac) in a controlled execution environment.  It also allows you to easily test the script with any combination of Nginx proxy host configurations and backing services that Nginx will reverse proxy to.\n\nThis repo contains everything you need to do just that.  It's set up to run Nginx as well as a simple backend server in individual [Docker](http://www.docker.com) containers.\n\n### Prerequisites\n\n#### Mac OS\n\n1. [Docker Toolbox](https://www.docker.com/toolbox)\n1. [Node.js](https://nodejs.org/)\n\n\u003e **IMPORTANT**: The test scripts expect your **Docker Toolbox** `docker-machine` VM name to be `default`\n\n#### Ubuntu\n\n1. [Docker](https://docs.docker.com/installation/ubuntulinux/)\n1. [Node.js](https://nodejs.org/)\n\nBesides being able to install Docker and run Docker directly in the host OS, the other different between Ubuntu (and more specifically Linux) and Mac OS is that all Docker commands need to be called using `sudo`. In the examples that follow, a helper script called `build` is used to perform all Docker commands and should therefore be prefixed with `sudo`, like this:\n\n```bash\nsudo ./build run\n```\n\n#### Ubuntu on MacOS (via Vagrant)\n\nIf your host OS is Mac OS but you'd like to test that the build scripts run on Ubuntu, you can use the provided Vagrant scripts to spin up an Ubuntu VM that has all the necessary tools installed.\n\nFirst, if you haven't already, install **Vagrant** either by [installing the package](http://www.vagrantup.com/downloads.html) or using [Homebrew](http://sourabhbajaj.com/mac-setup/Vagrant/README.html).\n\nThen in the repo directory, start the VM:\n\n```bash\nvagrant up\n```\n\nAnd then SSH into it:\n\n```bash\nvagrant ssh\n```\n\nOnce in, you'll need to use git to clone this repo and `cd` into the project:\n\n```bash\ngit clone THIS_REPO_URL\ncd nginx-jwt\n```\n\nAll other tools should be installed. And like with the [Ubuntu](#ubuntu) host OS, you'll need to prefix all calls to the `build` script with `sudo`, like this:\n\n```bash\nsudo ./build run\n```\n\n### Build and run the default containers\n\nIf you just want to see the **nginx-jwt** script in action, you can run the [`backend`](hosts/backend) container and the [`default`](hosts/proxy/default) proxy (Nginx) container:\n\n```bash\n./build run\n```\n\n\u003e **NOTE**: On the first run, the above script may take several minutes to download and build all the base Docker images, so go grab a fresh cup of coffee.  Successive runs are much faster.\n\nYou can then run cURL commands against the endpoints exposed by the backend through Nginx.  The root URL of the proxy is reported back by the script when it is finished.  It will look something like this:\n\n```\n...\nProxy:\ncurl http://192.168.59.103\n```\n\nNotice the proxy container (which is running in the Docker Machine VM) is listening on port 80.  The actual backend container is not directly accessible via the VM.  All calls are configured to reverse-proxy through the Nginx host and the connection between the two is done via [docker container linking](https://docs.docker.com/userguide/dockerlinks/).\n\nIf you issue the above cURL command, you'll hit the [proxy's root (`/`) endpoint](hosts/proxy/default/nginx/conf/nginx.conf#L14), which simply reverse-proxies to the [non-secure backend endpoint](hosts/backend/server.js#L7), which doesn't require any authentication:\n\n```bash\ncurl -i http://192.168.59.103\n```\n\n```\nHTTP/1.1 200 OK\nServer: openresty/1.7.7.1\nDate: Sun, 03 May 2015 18:05:10 GMT\nContent-Type: text/html; charset=utf-8\nContent-Length: 16\nConnection: keep-alive\nX-Powered-By: Express\nETag: W/\"10-574c3064\"\n\nBackend API root\n```\n\nHowever, if you attempt to cURL the [proxy's `/secure` endpoint](hosts/proxy/default/nginx/conf/nginx.conf#L18), you're going to get a `401` response from Nginx since it requires a valid JWT:\n\n```bash\ncurl -i http://192.168.59.103/secure\n```\n\n```\nHTTP/1.1 401 Unauthorized\nServer: openresty/1.7.7.1\nDate: Sun, 03 May 2015 18:05:00 GMT\nContent-Type: text/html\nContent-Length: 200\nConnection: keep-alive\n\n\u003chtml\u003e\n\u003chead\u003e\u003ctitle\u003e401 Authorization Required\u003c/title\u003e\u003c/head\u003e\n\u003cbody bgcolor=\"white\"\u003e\n\u003ccenter\u003e\u003ch1\u003e401 Authorization Required\u003c/h1\u003e\u003c/center\u003e\n\u003chr\u003e\u003ccenter\u003eopenresty/1.7.7.1\u003c/center\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nJust like we showed in the [Usage](#usage) section, we can use the included `sign` tool to generate a JWT and call the Nginx proxy again, this time with a `200` response:\n\n```bash\ntest/sign '{\"sub\": \"flynn\"}' 'JWTs are the best!'\n```\n\n```\nPayload: { sub: 'flynn' }\nSecret: JWTs are the best!\nToken: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwZXRlIiwiaWF0IjoxNDMwNjc3NjYzfQ.Zt4qnQyljbqLvAN7BQSuu14z5PjKcPpZZY85hDFVN3E\n```\n\n```bash\ncurl -i http://192.168.59.103/secure -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwZXRlIiwiaWF0IjoxNDMwNjc3NjYzfQ.Zt4qnQyljbqLvAN7BQSuu14z5PjKcPpZZY85hDFVN3E'\n```\n\n```\nHTTP/1.1 200 OK\nServer: openresty/1.7.7.1\nDate: Sun, 03 May 2015 18:34:18 GMT\nContent-Type: application/json; charset=utf-8\nContent-Length: 47\nConnection: keep-alive\nX-Auth-UserId: flynn\nX-Powered-By: Express\nETag: W/\"2f-8fc49de2\"\n\n{\"message\":\"This endpoint needs to be secure.\"}\n```\n\nThe proxy exposes other endpoints, which have different JWT requirements.  To see them all, take a look at the default proxy's [`nginx.conf`](hosts/proxy/default/nginx/conf/nginx.conf) file.\n\nIf you want to run the script with one of the [other proxy containers](hosts/proxy), simply pass the name of the desired container.  Example:\n\n```bash\n./build run base64-secret\n```\n\n### Build the containers and run integration tests\n\nThis script is similar to `run` except it executes all the [integration tests](test/test_integration.js), which end up building and running additional proxy containers to simulate different scenarios.\n\n```bash\n./build tests\n```\n\nUse this script while developing new features.\n\n### Clean everything up\n\nIf you need to simply stop/delete all running Docker containers and remove their associated images, use this command:\n\n```bash\n./build clean\n```\n\n### Updating dependencies\n\nIt's always nice to keep dependencies up to date. This library (and the tools used to test it) has three sources of dependencies that should be maintained: Lua dependencies, test script Node.js dependencies, and updates to the proxy base Docker image.\n\n#### Lua dependencies\n\nThese are the Lua scripts that [this library](nginx-jwt.lua) uses.  They are maintained in the [`build_deps.sh`](scripts/build_deps.sh) bash script.\n\nSince these dependencies don't have any built-in versioning (like npm), we download a specific GitHub commit instead. We also check that a previously downloaded script is current by examining its SHA-1 digest hash. All this is done via the included  `load_dependency` bash function.\n\nIf a Lua dependency needs to be updated, find its associated `load_dependency` function call and update its GitHub `commit` and `sha1` parameter values. You can generate the required SHA-1 digest of a new script file using this command:\n\n```bash\nopenssh sha1 NEW_SCRIPT\n```\n\nTo add a new dependency simply add a new `load_dependency` command to the script.\n\n#### Test script Node.js dependencies\n\nAll Node.js dependencies (npm packages) for tests are maintained in this [`package.json`](test/package.json) file and should be updated as needed using the `npm` command.\n\n#### Proxy base Docker image\n\nThe proxy base Docker image may need to be updated periodically, usually to just rev the version of OpenResty that its using. This can be done by modifying the image's [`Dockerfile`](hosts/proxy/Dockerfile). Any change to this file will automatically result in new image builds when the `build` script is run.\n\n## Packaging\n\nWhen a new version of the script needs to be released, the following should be done:\n\n\u003e **NOTE**: These steps can only performed by GitHub users with commit access to the project.\n\n1. Increment the [Semver](http://semver.org/) version in the [`version.txt`](version.txt) file as needed.\n1. Create a new git tag with the same version value (prefiexed with `v`):\n\n  ```bash\n  git tag v$(cat version.txt)\n  ```\n\n1. Push the tag to GitHub.\n1. Create a new GitHub release in [releases](https://github.com/auth0/nginx-jwt/releases) that's associated with the above tag.\n1. Run the following command to create a release package archive and then upload it to the release created above:  \n\n  ```bash\n  ./build package\n  ```\n\n## Issue Reporting\n\nIf you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues.\n\n## Contributors\n\nCheck them out [here](https://github.com/auth0/nginx-jwt/graphs/contributors).\n\n## License\n\nThis project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fauth0%2Fnginx-jwt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fauth0%2Fnginx-jwt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fauth0%2Fnginx-jwt/lists"}