UNPKG

atlassian-connect-express

Version:

Library for building Atlassian Add-ons on top of Express

107 lines (95 loc) 3.02 kB
const jose = require("jose"); const _ = require("lodash"); function authenticate(addon) { if (!addon.config.appId()) { throw new Error( "In order to use Forge authentication middleware, the app ID must be configured under" + "\n`appId` in config.json, or via the `AC_APP_ID` environment variable." + "\nThe app ID can be found in your `manifest.yml`" ); } return async function (req, res, next) { function sendError(msg) { const code = 401; addon.logger.error("Authentication failed:", code, msg); if (addon.config.expressErrorHandling()) { next({ code, message: msg }); } else { res.status(code).send(_.escape(msg)); } } function getJwksUrl(forgeInvocationToken) { const decodedClaims = jose.decodeJwt(forgeInvocationToken, { complete: true }); if ( decodedClaims && decodedClaims.app && decodedClaims.app.apiBaseUrl && decodedClaims.app.apiBaseUrl.startsWith( "https://api.stg.atlassian.com/" ) ) { return "https://forge.cdn.stg.atlassian-dev.net/.well-known/jwks.json"; } else { return "https://forge.cdn.prod.atlassian-dev.net/.well-known/jwks.json"; } } const verifyToken = async (req, appId) => { const ariPrefix = "ari:cloud:ecosystem::app/"; if (!appId.startsWith(ariPrefix)) { appId = ariPrefix + appId; } let forgeInvocationToken; const authHeader = req.headers.authorization; if (authHeader) { if (authHeader.toLowerCase().startsWith("bearer")) { [, forgeInvocationToken] = authHeader.split(" "); } } if (!forgeInvocationToken) { throw new Error("No valid auth token provided in the request"); } const jwksUrl = getJwksUrl(forgeInvocationToken); const JWKS = jose.createRemoteJWKSet(new URL(jwksUrl)); const payload = await jose.jwtVerify(forgeInvocationToken, JWKS, { audience: appId, issuer: "forge/invocation-token" }); return payload; }; try { const payload = await verifyToken(req, addon.config.appId()); const innerPayload = payload.payload || {}; const { installationId, apiBaseUrl } = innerPayload.app; const { "x-forge-oauth-system": appToken, "x-forge-oauth-user": userToken } = req.headers; await addon.forgeAppTokenCache.setForgeAppToken( installationId, appToken, apiBaseUrl ); req.context.forge = {}; Object.assign(req.context.forge, { tokens: { app: appToken, user: userToken }, ...innerPayload, apiBaseUrl }); next(); } catch (err) { console.error("Error while verifying Forge Invocation Token:", err); sendError("Forge Invocation Token verification was unsuccessful"); } }; } module.exports = { authenticate };