Socket
Socket
Sign inDemoInstall

@seibert/atlassian-connect-tooling

Package Overview
Dependencies
10
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.0-next.8 to 1.1.0

2

out/index.d.ts

@@ -65,3 +65,3 @@ /// <reference types="node" />

* @param fetchTenantByClientKey
* Method that asynchronously returns the data that you have stored for the tenant given its clientKey. Return data has to contain at least a "sharedSecret".
* Method that asynchronously returns the data that you have stored for the tenant given its clientKey. Returned data has to contain at least a "sharedSecret".
* Return null if no matching tenant can be found by your implementation.

@@ -68,0 +68,0 @@ * @param requestsForQshValidation (optional)

@@ -1,1 +0,1 @@

var H=Object.create;var y=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,Q=Object.prototype.hasOwnProperty;var A=e=>y(e,"__esModule",{value:!0});var F=(e,t)=>{A(e);for(var n in t)y(e,n,{get:t[n],enumerable:!0})},$=(e,t,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of D(t))!Q.call(e,r)&&r!=="default"&&y(e,r,{get:()=>t[r],enumerable:!(n=W(t,r))||n.enumerable});return e},f=e=>$(A(y(e!=null?H(K(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);F(exports,{composeAtlassianConnectInstallationMiddleware:()=>E,composeAtlassianRequestAuthenticationMiddleware:()=>P,createJwtForRequestAuthorization:()=>T});var J=f(require("atlassian-jwt")),g=f(require("atlassian-jwt"));var h=f(require("atlassian-jwt"));function x({baseUrl:e,qsh:t,request:n}){let r=(0,h.createQueryStringHash)(n,!1,e);if(t===r)return{successful:!0};let a=(0,h.createQueryStringHash)(n,!0,e);if(t===a)return{successful:!0};let s=(0,h.createCanonicalRequest)(n,!0,e),o=`Auth failure: Query hash mismatch: Received: "${t}" but calculated "${r} ${r!==a&&`and ${a}`}. Requests canonically expression was: "${s}"`;return{successful:!1,error:o}}function b(e,t,{expectedAudience:n,expectedIssuer:r,request:a}){let s=(0,J.decodeAsymmetric)(e,t,g.AsymmetricAlgorithm.RS256,!1),o=c=>c.replace(/\/$/,""),d=o(n);if(!s.aud||!Array.isArray(s.aud))throw new Error(`Jwt token invalid. It is missing a valid aud claim. ${s.aud}`);if(!s.aud.map(o).includes(d))throw new Error(`Jwt token invalid. Expected audience not part of jwt tokens audiences. (${s.aud})`);if(!s.iss)throw new Error("Jwt token invalid. It is missing an iss claim.");if(s.iss!==r)throw new Error("Jwt token invalid. Invalid issuer.");if(!s.qsh)throw new Error("Jwt token invalid. Does not contain qsh claim.");let{successful:u,error:l}=x({baseUrl:n,qsh:s.qsh,request:a});if(!u)throw new Error(`Jwt token invalid. Query hash mismatch. ${l}`);if(!s.exp||typeof s.exp!="number")throw new Error("Jwt token invalid. Does not contain exp claim.");if(Math.round(new Date().getTime()/1e3)>=s.exp)throw new Error("Jwt token invalid. Expired token.");return s}function j(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 v=f(require("node-fetch"));async function C({url:e}){let t=await(0,v.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 S=f(require("atlassian-jwt")),z="https://connect-install-keys.atlassian.com/";function E({baseUrl:e}){return function(n,r,a){var l;let s=n.get("Authorization");if(!s){r.status(401).send("Could not find Authorization header.");return}let o=s.substring(4),[d]=o.split("."),u=j(d);if(!(u==null?void 0:u.kid)){r.status(400).send("Jwt header has an unexpected form.");return}if(!((l=n.body)==null?void 0:l.clientKey)){r.status(401).send("Could not find clientKey on body.");return}C({url:z+u.kid}).then(i=>{try{n.validatedJwtPayload=b(o,i,{expectedAudience:e,expectedIssuer:n.body.clientKey,request:(0,S.fromExpressRequest)(n)}),a()}catch(c){r.status(401).send("Could not decode jwt token."+c)}}).catch(i=>{r.status(401).send("Could not obtain rsaPublicKey"+i)})}}function k(e){var s,o;let t=(s=e.query)==null?void 0:s.jwt,n=(o=e.body)==null?void 0:o.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 r=M(e);if(t&&n)return console.warn("JWT token can only appear in either query parameter or request body."),null;let a=t||n||r;return a||(console.warn("JWT token not found in request query, body or authorization header."),null)}function M(e){var n;let t=(n=e.headers)==null?void 0:n.authorization;return t&&t.indexOf("JWT ")===0?t.substring(4):null}var m=f(require("atlassian-jwt"));function P({baseUrl:e,fetchTenantByClientKey:t,requestsForQshValidation:n}){return function(a,s,o){let d=k(a);if(!d){s.status(401).send("Could not find JWT.");return}let u;try{u=(0,m.decodeSymmetric)(d,"",m.SymmetricAlgorithm.HS256,!0)}catch(i){s.status(401).send("Invalid JWT."+i);return}let l=u.iss;if(!l){s.status(401).send("JWT did not contain the issuer (iss) claim");return}t(l).then(i=>{if(!(i==null?void 0:i.sharedSecret)){s.status(401).send("Could not obtain shared secret stored for client");return}let c;try{c=(0,m.decodeSymmetric)(d,i.sharedSecret,m.SymmetricAlgorithm.HS256,!1)}catch(p){s.status(401).send("Unable to decode JWT."+p);return}if(!c.exp||typeof c.exp!="number"||Math.round(new Date().getTime()/1e3)>=c.exp){s.status(401).send("Jwt token invalid. Does not contain an valid exp claim.");return}let R=(n||[(0,m.fromExpressRequest)(a)]).map(p=>x({baseUrl:e,qsh:c.qsh,request:p})).reduce((p,q)=>({successful:q.successful||p.successful,errors:[...p.errors,q.error].filter(I=>I)}),{successful:!1,errors:[]});if(!R.successful){s.status(401).send("Jwt token invalid. Does not contain an valid qsh claim. "+R.errors.join(", "));return}a.atlassianVerified={clientKey:c.iss,userAccountId:c.sub,jwtPayload:c,tenant:i},o()}).catch(i=>{s.status(400).send("Request could not be validated."+i)})}}var w=f(require("atlassian-jwt"));function T({tenantData:e,issuer:t,expirationInSeconds:n=300,request:{method:r="GET",pathname:a="",body:s,query:o},baseUrl:d}){let u=Math.round(new Date().getTime()/1e3),l={iss:t,iat:u,exp:u+n,aud:[e.clientKey],qsh:(0,w.createQueryStringHash)({method:r,pathname:a,body:s,query:o},!!s,d)};return(0,w.encodeSymmetric)(l,e.sharedSecret,w.SymmetricAlgorithm.HS256)}0&&(module.exports={composeAtlassianConnectInstallationMiddleware,composeAtlassianRequestAuthenticationMiddleware,createJwtForRequestAuthorization});
var H=Object.create;var y=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,Q=Object.prototype.hasOwnProperty;var A=e=>y(e,"__esModule",{value:!0});var F=(e,t)=>{A(e);for(var n in t)y(e,n,{get:t[n],enumerable:!0})},$=(e,t,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of D(t))!Q.call(e,r)&&r!=="default"&&y(e,r,{get:()=>t[r],enumerable:!(n=W(t,r))||n.enumerable});return e},m=e=>$(A(y(e!=null?H(K(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);F(exports,{composeAtlassianConnectInstallationMiddleware:()=>E,composeAtlassianRequestAuthenticationMiddleware:()=>P,createJwtForRequestAuthorization:()=>T});var J=m(require("atlassian-jwt")),g=m(require("atlassian-jwt"));var h=m(require("atlassian-jwt"));function x({baseUrl:e,qsh:t,request:n}){let r=(0,h.createQueryStringHash)(n,!1,e);if(t===r)return{successful:!0};let a=(0,h.createQueryStringHash)(n,!0,e);if(t===a)return{successful:!0};let s=(0,h.createCanonicalRequest)(n,!0,e),o=`Auth failure: Query hash mismatch: Received: "${t}" but calculated "${r} ${r!==a&&`and ${a}`}. Requests canonically expression was: "${s}"`;return{successful:!1,error:o}}function b(e,t,{expectedAudience:n,expectedIssuer:r,request:a}){let s=(0,J.decodeAsymmetric)(e,t,g.AsymmetricAlgorithm.RS256,!1),o=u=>u.replace(/\/$/,""),d=o(n);if(!s.aud||!Array.isArray(s.aud))throw new Error(`Jwt token invalid. It is missing a valid aud claim. ${s.aud}`);if(!s.aud.map(o).includes(d))throw new Error(`Jwt token invalid. Expected audience not part of jwt tokens audiences. (${s.aud})`);if(!s.iss)throw new Error("Jwt token invalid. It is missing an iss claim.");if(s.iss!==r)throw new Error("Jwt token invalid. Invalid issuer.");if(!s.qsh)throw new Error("Jwt token invalid. Does not contain qsh claim.");let{successful:c,error:l}=x({baseUrl:n,qsh:s.qsh,request:a});if(!c)throw new Error(`Jwt token invalid. Query hash mismatch. ${l}`);if(!s.exp||typeof s.exp!="number")throw new Error("Jwt token invalid. Does not contain exp claim.");if(Math.floor(new Date().getTime()/1e3)>=s.exp)throw new Error("Jwt token invalid. Expired token.");return s}function j(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 v=m(require("node-fetch"));async function C({url:e}){let t=await(0,v.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 S=m(require("atlassian-jwt")),z="https://connect-install-keys.atlassian.com/";function E({baseUrl:e}){return function(n,r,a){var l;let s=n.get("Authorization");if(!s){r.status(401).send("Could not find Authorization header.");return}let o=s.substring(4),[d]=o.split("."),c=j(d);if(!(c==null?void 0:c.kid)){r.status(400).send("Jwt header has an unexpected form.");return}if(!((l=n.body)==null?void 0:l.clientKey)){r.status(401).send("Could not find clientKey on body.");return}C({url:z+c.kid}).then(i=>{try{n.validatedJwtPayload=b(o,i,{expectedAudience:e,expectedIssuer:n.body.clientKey,request:(0,S.fromExpressRequest)(n)}),a()}catch(u){r.status(401).send("Could not decode jwt token."+u)}}).catch(i=>{r.status(401).send("Could not obtain rsaPublicKey"+i)})}}function k(e){var s,o;let t=(s=e.query)==null?void 0:s.jwt,n=(o=e.body)==null?void 0:o.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 r=M(e);if(t&&n)return console.warn("JWT token can only appear in either query parameter or request body."),null;let a=t||n||r;return a||(console.warn("JWT token not found in request query, body or authorization header."),null)}function M(e){var n;let t=(n=e.headers)==null?void 0:n.authorization;return t&&t.indexOf("JWT ")===0?t.substring(4):null}var f=m(require("atlassian-jwt"));function P({baseUrl:e,fetchTenantByClientKey:t,requestsForQshValidation:n}){return function(a,s,o){let d=k(a);if(!d){s.status(401).send("Could not find JWT.");return}let c;try{c=(0,f.decodeSymmetric)(d,"",f.SymmetricAlgorithm.HS256,!0)}catch(i){s.status(401).send("Invalid JWT."+i);return}let l=c.iss;if(!l){s.status(401).send("JWT did not contain the issuer (iss) claim");return}t(l).then(i=>{if(!(i==null?void 0:i.sharedSecret)){s.status(401).send("Could not obtain shared secret stored for client");return}let u;try{u=(0,f.decodeSymmetric)(d,i.sharedSecret,f.SymmetricAlgorithm.HS256,!1)}catch(p){s.status(401).send("Unable to decode JWT."+p);return}if(!u.exp||typeof u.exp!="number"||Math.round(new Date().getTime()/1e3)>=u.exp){s.status(401).send("Jwt token invalid. Does not contain an valid exp claim.");return}let R=(n||[(0,f.fromExpressRequest)(a)]).map(p=>x({baseUrl:e,qsh:u.qsh,request:p})).reduce((p,q)=>({successful:q.successful||p.successful,errors:[...p.errors,q.error].filter(I=>I)}),{successful:!1,errors:[]});if(!R.successful){s.status(401).send("Jwt token invalid. Does not contain an valid qsh claim. "+R.errors.join(", "));return}a.atlassianVerified={clientKey:u.iss,userAccountId:u.sub,jwtPayload:u,tenant:i},o()}).catch(i=>{s.status(400).send("Request could not be validated."+i)})}}var w=m(require("atlassian-jwt"));function T({tenantData:e,issuer:t,expirationInSeconds:n=300,request:{method:r="GET",pathname:a="",body:s,query:o},baseUrl:d}){let c=Math.floor(new Date().getTime()/1e3),l={iss:t,iat:c,exp:c+n,aud:[e.clientKey],qsh:(0,w.createQueryStringHash)({method:r,pathname:a,body:s,query:o},!!s,d)};return(0,w.encodeSymmetric)(l,e.sharedSecret,w.SymmetricAlgorithm.HS256)}0&&(module.exports={composeAtlassianConnectInstallationMiddleware,composeAtlassianRequestAuthenticationMiddleware,createJwtForRequestAuthorization});
{
"name": "@seibert/atlassian-connect-tooling",
"version": "1.1.0-next.8",
"version": "1.1.0",
"description": "Provides authentication & utility methods for Atlassian Connect apps running on Express.",

@@ -5,0 +5,0 @@ "main": "out/index.js",

@@ -46,11 +46,30 @@ # Atlassian Connect Tooling

app.post('/lifecycle/installed/', [installAuthentication], async (req: Request, res: Response) => {
app.post('/lifecycle/installed/', [installAuthentication], (req: Request, res: Response) => {
// request is authenticated, process installation here. Necessary information are on request body.
await handleInstall(req.body);
res.send();
handleInstall(req.body).then(res.send);
});
app.post('/lifecycle/uninstalled/', [installAuthentication], async (req: Request, res: Response) => {
app.post('/lifecycle/uninstalled/', [installAuthentication], (req: Request, res: Response) => {
// request is authenticated, process uninstall here. Necessary information are on request body.
await handleUninstall(req.body);
handleUninstall(req.body).then(res.send);
});
```
### Composable request authentication middleware
This packages exports ```composeAtlassianRequestAuthenticationMiddleware``` to get a middleware function that handles
authentication of JWTs. The composed middleware verifies the requests as outlined in the Atlassian Connect documentation
using a JWT signed with the sharedSecret of a tenant. You have to provide an async. method (fetchTenantByClientKey) that
returns the client data you have persisted for the tenant. The object has to at least contain a sharedSecret.
```ts
const atlassianMiddlewareConfig = {
baseUrl: ATLASSIAN_CONNECT_FILE.baseUrl,
fetchTenantByClientKey(clientKey: string) {
return getInstallation(db, clientKey);
}
};
const atlassianAuthentication = composeAtlassianRequestAuthenticationMiddleware(atlassianMiddlewareConfig);
app.get("/macros/some-macro", [atlassianAuthentication], (req: Request & AuthenticatedAtlassianRequest, res: Response) => {
// request is authenticated, verified information on req.atlassianVerified
res.send();

@@ -60,4 +79,12 @@ });

### Create JWTs for custom authentication
This packages exports ```createJwtForRequestAuthorization``` which allows you to create own JWTs that may be accepted by
the middlewares functions from ```composeAtlassianRequestAuthenticationMiddleware```. See docs
of ```createJwtForRequestAuthorization``` for more information and code examples.
#### Example usage with Next.js
Express middlewares can be used with Next.js using a wrapper function. See their [Connect/Express middleware support documentation
Express middlewares can be used with Next.js using a wrapper function. See
their [Connect/Express middleware support documentation
](https://nextjs.org/docs/api-routes/api-middlewares#connectexpress-middleware-support).
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc