UNPKG

serviceberry-jwt

Version:

JSON Web Token authentication for Serviceberry

114 lines (88 loc) 2.85 kB
"use strict"; const jwt = require("jsonwebtoken"), {promisify} = require("util"), verify = promisify(jwt.verify), bearer = "Bearer ", methodsWithBody = ["POST", "PUT", "PATCH"]; class Jwt { constructor ({...verifyOptions} = {}, {...options} = {scheme: "Bearer"}) { Object.assign(this, {verifyOptions, options}); if (this.options.scheme === "Token" && !this.options.param) { this.options.param = "token"; } } async use (request) { var token = this.getToken(request); if (!token) { this.unauthorized(request, "Please provide token."); } request.jwt = Object.assign(Object.create(null), jwt.decode(token, { complete: true })); deepFreeze(Object.assign(request.jwt, { string: token })); if (!request.jwt.payload) { this.unauthorized(request, "Token is malformed."); } return this.validate(request); } async validate (request) { const key = await this.getKey(request.jwt.header.kid); try { await verify(request.jwt.string, key, this.verifyOptions); } catch (error) { this.unauthorized(request, error.message); } } getToken (request) { var {scheme, accessToken, param} = this.options, token; if (scheme === "Bearer" && accessToken) { token = this.getBearerToken(request); } else if (scheme === "Bearer") { token = this.getAuthHeaderToken(request); } else if (scheme === "Token") { token = request.getParam(param); } return token; } getBearerToken (request) { var token = this.getAuthHeaderToken(request); // Per [RFC 6750](https://tools.ietf.org/html/rfc6750) sections 2.2 & 2.3 // bearer tokens can be passed as access_token in the body if body is form encoded and method has body if (!token && methodsWithBody.includes(request.getMethod()) && request.getContentType() === "application/x-www-form-urlencoded") { token = request.getBodyParam("access_token"); // or as access_token in the query string } else if (!token) { token = request.getQueryParam("access_token"); } return token; } getAuthHeaderToken (request) { var token = request.getHeader("Authorization") || ""; if (token.startsWith(bearer)) { token = token.slice(bearer.length); } return token; } async getKey () { throw new Error( "serviceberry-jwt plugin exports an abstract class (Jwt). " + "Consumers of the plugin must extend this class and at least implement the getKey(id) method." ); } unauthorized (request, message) { request.fail(message, "Unauthorized", { "WWW-Authenticate": this.options.scheme }); } } function deepFreeze (value) { if (value === Object(value)) { Object.getOwnPropertyNames(Object.freeze(value)) .forEach(name => deepFreeze(value[name])); } } module.exports = Jwt;