UNPKG

@seibert/atlassian-connect-tooling

Version:

Provides authentication & utility methods for Atlassian Connect apps running on Express.

2 lines (1 loc) 6.46 kB
var F=Object.create;var y=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames,J=Object.getOwnPropertySymbols,O=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty,V=Object.prototype.propertyIsEnumerable;var b=(e,t,s)=>t in e?y(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s,j=(e,t)=>{for(var s in t||(t={}))A.call(t,s)&&b(e,s,t[s]);if(J)for(var s of J(t))V.call(t,s)&&b(e,s,t[s]);return e};var v=e=>y(e,"__esModule",{value:!0});var N=(e,t)=>{v(e);for(var s in t)y(e,s,{get:t[s],enumerable:!0})},B=(e,t,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of $(t))!A.call(e,n)&&n!=="default"&&y(e,n,{get:()=>t[n],enumerable:!(s=M(t,n))||s.enumerable});return e},f=e=>B(v(y(e!=null?F(O(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);N(exports,{composeAtlassianConnectInstallationMiddleware:()=>z,composeAtlassianRequestAuthenticationMiddleware:()=>D,createJwtForRequestAuthorization:()=>K});var C=f(require("atlassian-jwt")),E=f(require("atlassian-jwt"));var x=f(require("atlassian-jwt"));function g({baseUrl:e,qsh:t,request:s}){let n=(0,x.createQueryStringHash)(s,!1,e);if(t===n)return{successful:!0};let o=(0,x.createQueryStringHash)(s,!0,e);if(t===o)return{successful:!0};let r=(0,x.createCanonicalRequest)(s,!0,e),c=`Auth failure: Query hash mismatch: Received: "${t}" but calculated "${n} ${n!==o&&`and ${o}`}. Requests canonically expression was: "${r}"`;return{successful:!1,error:c}}function S(e,t,{expectedAudience:s,expectedIssuer:n,request:o}){let r=(0,C.decodeAsymmetric)(e,t,E.AsymmetricAlgorithm.RS256,!1),c=u=>u.replace(/\/$/,""),m=c(s);if(!r.aud||!Array.isArray(r.aud))throw new Error(`Jwt token invalid. It is missing a valid aud claim. ${r.aud}`);if(!r.aud.map(c).includes(m))throw new Error(`Jwt token invalid. Expected audience not part of jwt tokens audiences. (${r.aud})`);if(!r.iss)throw new Error("Jwt token invalid. It is missing an iss claim.");if(r.iss!==n)throw new Error("Jwt token invalid. Invalid issuer.");if(!r.qsh)throw new Error("Jwt token invalid. Does not contain qsh claim.");let{successful:l,error:d}=g({baseUrl:s,qsh:r.qsh,request:o});if(!l)throw new Error(`Jwt token invalid. Query hash mismatch. ${d}`);if(!r.exp||typeof r.exp!="number")throw new Error("Jwt token invalid. Does not contain exp claim.");if(Math.floor(new Date().getTime()/1e3)>=r.exp)throw new Error("Jwt token invalid. Expired token.");return r}function k(e){try{return JSON.parse(Buffer.from(e,"base64").toString())}catch(t){return console.error("Could not decode JWT header. Returning null instead.",t),null}}var P=f(require("node-fetch"));async function T({url:e}){let t=await(0,P.default)(e);if(!t.ok)throw new Error(`Could not fetch RSA public key. Host responded with ${t.status}. ${t.statusText}.`);return await t.text()}var H=f(require("atlassian-jwt"));var I=f(require("sanitize-html"));function i({status:e=401,message:t,res:s}){let n=(0,I.default)(t,{allowedTags:[],allowedAttributes:{}});s.status(e).send(n)}var U="https://connect-install-keys.atlassian.com/";function z({baseUrl:e}){return function(s,n,o){var d;let r=s.get("Authorization");if(!r){i({res:n,message:"Could not find Authorization header."});return}let c=r.substring(4),[m]=c.split("."),l=k(m);if(!(l==null?void 0:l.kid)){i({res:n,status:400,message:"Jwt header has an unexpected form."});return}if(!((d=s.body)==null?void 0:d.clientKey)){i({res:n,message:"Could not find clientKey on body."});return}T({url:U+l.kid}).then(a=>{try{s.validatedJwtPayload=S(c,a,{expectedAudience:e,expectedIssuer:s.body.clientKey,request:(0,H.fromExpressRequest)(s)}),o()}catch(u){i({res:n,message:"Could not decode jwt token."+u})}}).catch(a=>{i({res:n,message:"Could not obtain rsaPublicKey"+a})})}}function W(e){var r,c;let t=(r=e.query)==null?void 0:r.jwt,s=(c=e.body)==null?void 0:c.jwt;if(!t&&!e.body)return console.warn("Cannot find JWT token in query parameters. Please include body-parser middleware and parse the urlencoded body (See https://github.com/expressjs/body-parser) if the add-on is rendering in POST mode. Otherwise please ensure the jwt parameter is presented in query."),null;let n=X(e);if(t&&s)return console.warn("JWT token can only appear in either query parameter or request body."),null;let o=t||s||n;return o||(console.warn("JWT token not found in request query, body or authorization header."),null)}function X(e){var s;let t=(s=e.headers)==null?void 0:s.authorization;return t&&t.indexOf("JWT ")===0?t.substring(4):null}var p=f(require("atlassian-jwt"));function D({baseUrl:e,fetchTenantByClientKey:t,requestsForQshValidation:s}){return function(o,r,c){let m=W(o);if(!m){i({res:r,message:"Could not find JWT."});return}let l;try{l=(0,p.decodeSymmetric)(m,"",p.SymmetricAlgorithm.HS256,!0)}catch(a){i({res:r,message:"Invalid JWT."+a});return}let d=l.iss;if(!d){i({res:r,message:"JWT did not contain the issuer (iss) claim"});return}t(d).then(a=>{if(!(a==null?void 0:a.sharedSecret)){i({res:r,message:"Could not obtain shared secret stored for client"});return}let u;try{u=(0,p.decodeSymmetric)(m,a.sharedSecret,p.SymmetricAlgorithm.HS256,!1)}catch(w){i({res:r,message:"Unable to decode JWT."+w});return}if(!u.exp||typeof u.exp!="number"||Math.round(new Date().getTime()/1e3)>=u.exp){i({res:r,message:"Jwt token invalid. Does not contain an valid exp claim."});return}let R=(s||[(0,p.fromExpressRequest)(o)]).map(w=>g({baseUrl:e,qsh:u.qsh,request:w})).reduce((w,q)=>({successful:q.successful||w.successful,errors:[...w.errors,q.error].filter(Q=>Q)}),{successful:!1,errors:[]});if(!R.successful){i({res:r,message:"Jwt token invalid. Does not contain an valid qsh claim. "+R.errors.join(", ")});return}o.atlassianVerified={clientKey:u.iss,userAccountId:u.sub,jwtPayload:u,tenant:a},c()}).catch(a=>{i({res:r,status:400,message:"Request could not be validated."+a})})}}var h=f(require("atlassian-jwt"));function K({tenantData:e,issuer:t,expirationInSeconds:s=300,request:{method:n="GET",pathname:o="",body:r,query:c},baseUrl:m,additionalClaims:l={}}){let d=Math.floor(new Date().getTime()/1e3),a=j({iss:t,iat:d,exp:d+s,aud:[e.clientKey],qsh:(0,h.createQueryStringHash)({method:n,pathname:o,body:r,query:c},!!r,m)},l);return(0,h.encodeSymmetric)(a,e.sharedSecret,h.SymmetricAlgorithm.HS256)}0&&(module.exports={composeAtlassianConnectInstallationMiddleware,composeAtlassianRequestAuthenticationMiddleware,createJwtForRequestAuthorization});