koa-jwt2
v1.0.3
Published
JWT authentication middleware.
Downloads
24,783
Maintainers
Readme
koa-jwt2
Koa middleware that validates JsonWebTokens and sets ctx.state.user
.
This module lets you authenticate HTTP requests using JWT tokens in your Node.js applications. JWTs are typically used to protect API endpoints, and are often issued using OpenID Connect.
Install
$ npm install koa-jwt2 --save
Usage
The JWT authentication middleware authenticates callers using a JWT.
If the token is valid, ctx.state.user
will be set with the JSON object decoded
to be used by later middleware for authorization and access control.
For example,
var jwt = require("koa-jwt2");
app.get("/protected", jwt({ secret: "shhhhhhared-secret" }), async function(
ctx
) {
if (!ctx.state.user.admin) return (ctx.status = 401);
ctx.status = 200;
});
You can specify audience and/or issuer as well:
jwt({
secret: "shhhhhhared-secret",
audience: "http://myapi/protected",
issuer: "http://issuer"
});
If the JWT has an expiration (
exp
), it will be checked.
If you are using a base64 URL-encoded secret, pass a Buffer
with base64
encoding as the secret instead of a string:
jwt({ secret: new Buffer("shhhhhhared-secret", "base64") });
Optionally you can make some paths unprotected as follows:
app.use(jwt({ secret: "shhhhhhared-secret" }).unless({ path: ["/token"] }));
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.
For more details on the
.unless
syntax including additional options, please see koa-unless.
This module also support tokens signed with public/private key pairs. Instead of a secret, you can specify a Buffer with the public key
var publicKey = fs.readFileSync("/path/to/public.pub");
jwt({ secret: publicKey });
By default, the decoded token is attached to ctx.state.user
but can be configured with the property
option.
jwt({ secret: publicKey, property: "auth" });
A custom function for extracting the token from a request can be specified with
the getToken
option. This is useful if you need to pass the token through a
query parameter or a cookie. You can throw an error in this function and it will
be handled by koa-jwt2
.
app.use(
jwt({
secret: "hello world !",
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring(ctx) {
if (
ctx.headers.authorization &&
ctx.headers.authorization.split(" ")[0] === "Bearer"
) {
return ctx.headers.authorization.split(" ")[1];
} else if (ctx.query && ctx.query.token) {
return ctx.query.token;
}
return null;
}
})
);
Multi-tenancy
If you are developing an application in which the secret used to sign tokens is not static, you can provide a async function as the secret
parameter. The function has the signature: async function(ctx, payload)
:
ctx
(Object
) - The koactx
object.payload
(Object
) - An object with the JWT claims.
need to return a secret string or promise to use to verify the JWT.
For example, if the secret varies based on the JWT issuer:
const jwt = require("koa-jwt2");
const data = require("./data");
const utilities = require("./utilities");
const secretAsync = async function(ctx, payload) {
const issuer = payload.iss;
return new Promise((resolve, reject) => {
data.getTenantByIdentifier(issuer, function(err, tenant) {
if (err) {
return reject(err);
}
if (!tenant) {
reject(new Error("missing_secret"));
}
const secret = utilities.decrypt(tenant.secret);
resolve(secret);
});
});
};
app.get("/protected", jwt({ secret: secretCallback }), async function(ctx) {
if (!ctx.state.user.admin) {
ctx.throw(401);
}
ctx.status = 200;
ctx.body = "";
});
Revoked tokens
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 async function(ctx, payload)
:
ctx
(Object
) - The koacontext
object.payload
(Object
) - An object with the JWT claims.
For example, if the (iss, jti)
claim pair is used to identify a JWT:
const jwt = require("koa-jwt2");
const data = require("./data");
const utilities = require("./utilities");
const isRevokedAsync = function(req, payload, done) {
const issuer = payload.iss;
const tokenId = payload.jti;
return new Promise((resolve, reject) => {
data.getRevokedToken(issuer, tokenId, function(err, token) {
if (err) {
return reject(err);
}
resolve(!!token);
});
});
};
app.get(
"/protected",
jwt({
secret: "shhhhhhared-secret",
isRevoked: isRevokedAsync
}),
async function(ctx) {
if (!ctx.state.user.admin) {
ctx.throw(401);
}
ctx.status = 200;
ctx.body = "";
}
);
Error handling
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:
app.use(async function(ctx, next) {
try {
await next();
} catch (err) {
if (err.name === "UnauthorizedError") {
ctx.status = 401;
ctx.body = "invalid token...";
}
}
});
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:
app.use(
jwt({
secret: "hello world !",
credentialsRequired: false
})
);
Related Modules
- jsonwebtoken — JSON Web Token sign and verification
Tests
$ npm install
$ npm test
Contributors
Check them out here
License
This project is licensed under the MIT license. See the LICENSE file for more info.