atlassian-connect-express
Version:
Library for building Atlassian Add-ons on top of Express
107 lines (95 loc) • 3.02 kB
JavaScript
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
};