1 | # express-jwt
|
2 |
|
3 | This module provides Express middleware for validating JWTs ([JSON Web Tokens](https://jwt.io)) through the [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken/) module. The decoded JWT payload is available on the request object.
|
4 |
|
5 | ## Install
|
6 |
|
7 | ```
|
8 | $ npm install express-jwt
|
9 | ```
|
10 |
|
11 | ## API
|
12 |
|
13 | `expressjwt(options)`
|
14 |
|
15 | Options has the following paramters:
|
16 |
|
17 | - `secret: jwt.Secret | GetVerificationKey` (required): The secret as an string or a function to retrieve the secret.
|
18 | - `getToken?: TokenGetter` (optional): A function that receives the express `Request` and returns the token, by default it looks in the `Authorization` header.
|
19 | - `isRevoked?: IsRevoked` (optional): A function to verify if a token is revoked.
|
20 | - `credentialsRequired?: boolean` (optional): If its false, continue to the next middleware if the request does not contain a token instead of failing, defaults to true.
|
21 | - `requestProperty?: string` (optional): name of the property in the request object where the payload is set. Default to `req.auth`.
|
22 | - Plus... all the options available in the [jsonwebtoken verify function](https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback).
|
23 |
|
24 | The available functions have the following interface:
|
25 |
|
26 | - `GetVerificationKey = (req: express.Request, token: jwt.Jwt | undefined) => Promise<jwt.Secret>;`
|
27 | - `IsRevoked = (req: express.Request, token: jwt.Jwt | undefined) => Promise<boolean>;`
|
28 | - `TokenGetter = (req: express.Request) => string | Promise<string> | undefined;`
|
29 |
|
30 | ## Usage
|
31 |
|
32 | Basic usage using an HS256 secret:
|
33 |
|
34 | ```javascript
|
35 | var { expressjwt: jwt } = require("express-jwt");
|
36 | // or ES6
|
37 | // import { expressjwt, ExpressJwtRequest } from "express-jwt";
|
38 |
|
39 | app.get(
|
40 | "/protected",
|
41 | jwt({ secret: "shhhhhhared-secret", algorithms: ["HS256"] }),
|
42 | function (req, res) {
|
43 | if (!req.auth.admin) return res.sendStatus(401);
|
44 | res.sendStatus(200);
|
45 | }
|
46 | );
|
47 | ```
|
48 |
|
49 | The decoded JWT payload is available on the request via the `auth` property.
|
50 |
|
51 | > The default behavior of the module is to extract the JWT from the `Authorization` header as an [OAuth2 Bearer token](https://oauth.net/2/bearer-tokens/).
|
52 |
|
53 | ### Required Parameters
|
54 |
|
55 | The `algorithms` parameter is required to prevent potential downgrade attacks when providing third party libraries as **secrets**.
|
56 |
|
57 | :warning: **Do not mix symmetric and asymmetric (ie HS256/RS256) algorithms**: Mixing algorithms without further validation can potentially result in downgrade vulnerabilities.
|
58 |
|
59 | ```javascript
|
60 | jwt({
|
61 | secret: "shhhhhhared-secret",
|
62 | algorithms: ["HS256"],
|
63 | //algorithms: ['RS256']
|
64 | });
|
65 | ```
|
66 |
|
67 | ### Additional Options
|
68 |
|
69 | You can specify audience and/or issuer as well, which is highly recommended for security purposes:
|
70 |
|
71 | ```javascript
|
72 | jwt({
|
73 | secret: "shhhhhhared-secret",
|
74 | audience: "http://myapi/protected",
|
75 | issuer: "http://issuer",
|
76 | algorithms: ["HS256"],
|
77 | });
|
78 | ```
|
79 |
|
80 | > If the JWT has an expiration (`exp`), it will be checked.
|
81 |
|
82 | If you are using a base64 URL-encoded secret, pass a `Buffer` with `base64` encoding as the secret instead of a string:
|
83 |
|
84 | ```javascript
|
85 | jwt({
|
86 | secret: Buffer.from("shhhhhhared-secret", "base64"),
|
87 | algorithms: ["RS256"],
|
88 | });
|
89 | ```
|
90 |
|
91 | To only protect specific paths (e.g. beginning with `/api`), use [express router](https://expressjs.com/en/4x/api.html#app.use) call `use`, like so:
|
92 |
|
93 | ```javascript
|
94 | app.use("/api", jwt({ secret: "shhhhhhared-secret", algorithms: ["HS256"] }));
|
95 | ```
|
96 |
|
97 | Or, the other way around, if you want to make some paths unprotected, cal `unless` like so.
|
98 |
|
99 | ```javascript
|
100 | app.use(
|
101 | jwt({
|
102 | secret: "shhhhhhared-secret",
|
103 | algorithms: ["HS256"],
|
104 | }).unless({ path: ["/token"] })
|
105 | );
|
106 | ```
|
107 |
|
108 | This is especially useful when applying to multiple routes. In the example above, `path` can be a string, a regexp, or an array of any of those.
|
109 |
|
110 | > For more details on the `.unless` syntax including additional options, please see [express-unless](https://github.com/jfromaniello/express-unless).
|
111 |
|
112 | This module also support tokens signed with public/private key pairs. Instead of a secret, you can specify a Buffer with the public key
|
113 |
|
114 | ```javascript
|
115 | var publicKey = fs.readFileSync("/path/to/public.pub");
|
116 | jwt({ secret: publicKey, algorithms: ["RS256"] });
|
117 | ```
|
118 |
|
119 | ### Customizing Token Location
|
120 |
|
121 | A custom function for extracting the token from a request can be specified with
|
122 | the `getToken` option. This is useful if you need to pass the token through a
|
123 | query parameter or a cookie. You can throw an error in this function and it will
|
124 | be handled by `express-jwt`.
|
125 |
|
126 | ```javascript
|
127 | app.use(
|
128 | jwt({
|
129 | secret: "hello world !",
|
130 | algorithms: ["HS256"],
|
131 | credentialsRequired: false,
|
132 | getToken: function fromHeaderOrQuerystring(req) {
|
133 | if (
|
134 | req.headers.authorization &&
|
135 | req.headers.authorization.split(" ")[0] === "Bearer"
|
136 | ) {
|
137 | return req.headers.authorization.split(" ")[1];
|
138 | } else if (req.query && req.query.token) {
|
139 | return req.query.token;
|
140 | }
|
141 | return null;
|
142 | },
|
143 | })
|
144 | );
|
145 | ```
|
146 |
|
147 | ### Retrieve key dynamically
|
148 |
|
149 | If you need to obtain the key dynamically from other sources, you can pass a function in the `secret` parameter with the following parameters:
|
150 |
|
151 | - `req` (`Object`) - The express `request` object.
|
152 | - `token` (`Object`) - An object with the JWT payload and headers.
|
153 |
|
154 | For example, if the secret varies based on the [issuer](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#issDef):
|
155 |
|
156 | ```javascript
|
157 | var jwt = require("express-jwt");
|
158 | var data = require("./data");
|
159 | var utilities = require("./utilities");
|
160 |
|
161 | var getSecret = async function (req, token) {
|
162 | const issuer = token.payload.iss;
|
163 | const tenant = await data.getTenantByIdentifier(issuer);
|
164 | if (!tenant) {
|
165 | throw new Error("missing_secret");
|
166 | }
|
167 | return utilities.decrypt(tenant.secret);
|
168 | };
|
169 |
|
170 | app.get(
|
171 | "/protected",
|
172 | jwt({ secret: getSecret, algorithms: ["HS256"] }),
|
173 | function (req, res) {
|
174 | if (!req.auth.admin) return res.sendStatus(401);
|
175 | res.sendStatus(200);
|
176 | }
|
177 | );
|
178 | ```
|
179 |
|
180 | ### Revoked tokens
|
181 |
|
182 | It is possible that some tokens will need to be revoked so they cannot be used any longer. You can provide a function as the `isRevoked` option. The signature of the function is `function(req, payload, done)`:
|
183 |
|
184 | - `req` (`Object`) - The express `request` object.
|
185 | - `token` (`Object`) - An object with the JWT payload and headers.
|
186 |
|
187 | For example, if the `(iss, jti)` claim pair is used to identify a JWT:
|
188 |
|
189 | ```javascript
|
190 | const jwt = require("express-jwt");
|
191 | const data = require("./data");
|
192 |
|
193 | const isRevokedCallback = async (req, token) => {
|
194 | const issuer = token.payload.iss;
|
195 | const tokenId = token.payload.jti;
|
196 | const token = await data.getRevokedToken(issuer, tokenId);
|
197 | return token !== "undefined";
|
198 | };
|
199 |
|
200 | app.get(
|
201 | "/protected",
|
202 | jwt({
|
203 | secret: "shhhhhhared-secret",
|
204 | algorithms: ["HS256"],
|
205 | isRevoked: isRevokedCallback,
|
206 | }),
|
207 | function (req, res) {
|
208 | if (!req.auth.admin) return res.sendStatus(401);
|
209 | res.sendStatus(200);
|
210 | }
|
211 | );
|
212 | ```
|
213 |
|
214 | ### Error handling
|
215 |
|
216 | The default behavior is to throw an error when the token is invalid, so you can add your custom logic to manage unauthorized access as follows:
|
217 |
|
218 | ```javascript
|
219 | app.use(function (err, req, res, next) {
|
220 | if (err.name === "UnauthorizedError") {
|
221 | res.status(401).send("invalid token...");
|
222 | } else {
|
223 | next(err);
|
224 | }
|
225 | });
|
226 | ```
|
227 |
|
228 | You might want to use this module to identify registered users while still providing access to unregistered users. You can do this by using the option `credentialsRequired`:
|
229 |
|
230 | ```javascript
|
231 | app.use(
|
232 | jwt({
|
233 | secret: "hello world !",
|
234 | algorithms: ["HS256"],
|
235 | credentialsRequired: false,
|
236 | })
|
237 | );
|
238 | ```
|
239 |
|
240 | ## Typescript
|
241 |
|
242 | An `ExpressJwtRequest` type is provided which extends `express.Request` with the `auth` property.
|
243 |
|
244 | ```typescript
|
245 | import { expressjwt, ExpressJwtRequest } from "express-jwt";
|
246 |
|
247 | app.get(
|
248 | "/protected",
|
249 | expressjwt({ secret: "shhhhhhared-secret", algorithms: ["HS256"] }),
|
250 | function (req: ExpressJwtRequest, res: express.Response) {
|
251 | if (!req.auth.admin) return res.sendStatus(401);
|
252 | res.sendStatus(200);
|
253 | }
|
254 | );
|
255 | ```
|
256 |
|
257 | ## Migration from v6
|
258 |
|
259 | 1. The middleware function is now available as a named import rather than a default one: import { expressjwt } from 'express-jwt'
|
260 | 2. The decoded JWT payload is now available as req.auth rather than req.user
|
261 | 3. The `secret` function had `(req, header, payload, cb)`, now it can return a promise and receives `(req, token)`. `token` has `header` and `payload`.
|
262 | 4. The `isRevoked` function had `(req, payload, cb)`, now it can return a promise and receives `(req, token)`. `token` has `header` and `payload`.
|
263 |
|
264 | ## Related Modules
|
265 |
|
266 | - [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) — JSON Web Token sign and verification
|
267 | - [express-jwt-permissions](https://github.com/MichielDeMey/express-jwt-permissions) - Permissions middleware for JWT tokens
|
268 |
|
269 | ## Tests
|
270 |
|
271 | ```
|
272 | $ npm install
|
273 | $ npm test
|
274 | ```
|
275 |
|
276 | ## Contributors
|
277 |
|
278 | Check them out [here](https://github.com/auth0/express-jwt/graphs/contributors)
|
279 |
|
280 | ## Issue Reporting
|
281 |
|
282 | If 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.
|
283 |
|
284 | ## Author
|
285 |
|
286 | [Auth0](https://auth0.com)
|
287 |
|
288 | ## License
|
289 |
|
290 | This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.
|