UNPKG

@graphql-yoga/plugin-jwt

Version:
135 lines (134 loc) 5.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useJWT = useJWT; const tslib_1 = require("tslib"); const jsonwebtoken_1 = tslib_1.__importDefault(require("jsonwebtoken")); const config_js_1 = require("./config.js"); require("@whatwg-node/server-plugin-cookies"); const graphql_1 = require("graphql"); const utils_js_1 = require("./utils.js"); function useJWT(options) { let logger; const normalizedOptions = (0, config_js_1.normalizeConfig)(options); const payloadByRequest = new WeakMap(); const lookupToken = async (payload) => { for (const lookupLocation of normalizedOptions.tokenLookupLocations) { const token = await lookupLocation(payload); if (token) { return token; } } return null; }; const getSigningKey = async (kid) => { for (const provider of normalizedOptions.singingKeyProviders) { try { const key = await provider(kid); if (key) { return key; } } catch (e) { logger.error(`Failed to fetch signing key from signing provided:`, e); } } return null; }; return { onYogaInit({ yoga }) { logger = yoga.logger; }, async onRequestParse(payload) { // Try to find token in request, and reject the request if needed. const lookupResult = await lookupToken(payload); if (!lookupResult) { // If token is missing, we can reject the request based on the configuration. if (normalizedOptions.reject.missingToken) { logger.debug(`Token is missing in incoming HTTP request, JWT plugin failed to locate.`); throw (0, utils_js_1.unauthorizedError)(`Unauthenticated`); } return; } try { // Decode the token first, in order to get the key id to use. let decodedToken; try { decodedToken = jsonwebtoken_1.default.decode(lookupResult.token, { complete: true }); } catch (e) { logger.warn(`Failed to decode JWT authentication token: `, e); throw (0, utils_js_1.badRequestError)(`Invalid authentication token provided`); } if (!decodedToken) { logger.warn(`Failed to extract payload from incoming token, please make sure the token is a valid JWT.`); throw (0, utils_js_1.badRequestError)(`Invalid authentication token provided`); } // Fetch the signing key based on the key id. const signingKey = await getSigningKey(decodedToken?.header.kid); if (!signingKey) { logger.warn(`Signing key is not available for the key id: ${decodedToken?.header.kid}. Please make sure signing key providers are configured correctly.`); throw Error(`Authentication is not available at the moment.`); } // Verify the token with the signing key. const verified = await verify(logger, lookupResult.token, signingKey, normalizedOptions.tokenVerification); if (!verified) { logger.debug(`Token failed to verify, JWT plugin failed to authenticate.`); throw (0, utils_js_1.unauthorizedError)(`Unauthenticated`); } if (verified) { // Link the verified payload with the request (see `onContextBuilding` for the reading part) payloadByRequest.set(payload.request, { payload: verified, token: { value: lookupResult.token, prefix: lookupResult.prefix, }, }); } } catch (e) { // User-facing errors should be handled based on the configuration. // These errors are handled based on the value of "reject.invalidToken" config. if (e instanceof graphql_1.GraphQLError) { if (normalizedOptions.reject.invalidToken) { throw e; } return; } // Server/internal errors should be thrown, so they can be handled by the error handler and be masked. throw e; } }, onContextBuilding({ context, extendContext }) { if (normalizedOptions.extendContextFieldName === null) { return; } if (context.request == null) { throw new Error('Request is not available on context! Make sure you use this plugin with GraphQL Yoga.'); } // Get the payload and inject it into the GraphQL context. const result = payloadByRequest.get(context.request); if (result && normalizedOptions.extendContextFieldName) { extendContext({ [normalizedOptions.extendContextFieldName]: { payload: result.payload, token: result.token, }, }); } }, }; } function verify(logger, token, signingKey, options) { return new Promise((resolve, reject) => { jsonwebtoken_1.default.verify(token, signingKey, options, (err, result) => { if (err) { logger.warn(`Failed to verify authentication token: `, err); reject((0, utils_js_1.unauthorizedError)('Unauthenticated')); } else { resolve(result); } }); }); }