From aba71916c00618f769c9535ac2dedc4f9f8d6b83 Mon Sep 17 00:00:00 2001 From: Krzysztof Wolski Date: Fri, 29 Jul 2022 17:00:31 +0200 Subject: [PATCH 1/4] Add jwt packages --- package.json | 3 + pnpm-lock.yaml | 197 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 197 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c732dd4..e9dc7c4 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,12 @@ "fast-glob": "^3.2.11", "graphql": "^16.5.0", "jose": "^4.8.3", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^2.1.4", "retes": "^0.29.4" }, "devDependencies": { + "@types/jsonwebtoken": "^8.5.8", "@types/node": "^18.0.4", "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.30.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19569c5..f257ecb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,6 +1,7 @@ lockfileVersion: 5.4 specifiers: + '@types/jsonwebtoken': ^8.5.8 '@types/node': ^18.0.4 '@typescript-eslint/eslint-plugin': ^5.17.0 '@typescript-eslint/parser': ^5.30.7 @@ -18,6 +19,8 @@ specifiers: graphql: ^16.5.0 husky: ^8.0.1 jose: ^4.8.3 + jsonwebtoken: ^8.5.1 + jwks-rsa: ^2.1.4 prettier: 2.6.2 retes: ^0.29.4 tsm: ^2.2.1 @@ -30,9 +33,12 @@ dependencies: fast-glob: 3.2.11 graphql: 16.5.0 jose: 4.8.3 + jsonwebtoken: 8.5.1 + jwks-rsa: 2.1.4 retes: 0.29.4 devDependencies: + '@types/jsonwebtoken': 8.5.8 '@types/node': 18.0.4 '@typescript-eslint/eslint-plugin': 5.30.7_j3fwdls2rzb3mq7xmqckumqsle '@typescript-eslint/parser': 5.30.7_4hx5bygx4rxgd7xwyndf6ymwce @@ -121,6 +127,11 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.13.0 + /@panva/asn1.js/1.0.0: + resolution: {integrity: sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==} + engines: {node: '>=10.13.0'} + dev: false + /@pkgr/utils/2.3.0: resolution: {integrity: sha512-7dIJ9CRVzBnqyEl7diUHPUFJf/oty2SeoVzcMocc5PeOUDK9KGzvgIBjGRRzzlRDaOjh3ADwH0WeibQvi3ls2Q==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -133,6 +144,36 @@ packages: tslib: 2.4.0 dev: true + /@types/body-parser/1.19.2: + resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} + dependencies: + '@types/connect': 3.4.35 + '@types/node': 18.0.4 + dev: false + + /@types/connect/3.4.35: + resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} + dependencies: + '@types/node': 18.0.4 + dev: false + + /@types/express-serve-static-core/4.17.30: + resolution: {integrity: sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==} + dependencies: + '@types/node': 18.0.4 + '@types/qs': 6.9.7 + '@types/range-parser': 1.2.4 + dev: false + + /@types/express/4.17.13: + resolution: {integrity: sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==} + dependencies: + '@types/body-parser': 1.19.2 + '@types/express-serve-static-core': 4.17.30 + '@types/qs': 6.9.7 + '@types/serve-static': 1.13.10 + dev: false + /@types/json-schema/7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true @@ -141,9 +182,32 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true + /@types/jsonwebtoken/8.5.8: + resolution: {integrity: sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==} + dependencies: + '@types/node': 18.0.4 + + /@types/mime/1.3.2: + resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} + dev: false + /@types/node/18.0.4: resolution: {integrity: sha512-M0+G6V0Y4YV8cqzHssZpaNCqvYwlCiulmm0PwpNLF55r/+cT8Ol42CHRU1SEaYFH2rTwiiE1aYg/2g2rrtGdPA==} - dev: true + + /@types/qs/6.9.7: + resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} + dev: false + + /@types/range-parser/1.2.4: + resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} + dev: false + + /@types/serve-static/1.13.10: + resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==} + dependencies: + '@types/mime': 1.3.2 + '@types/node': 18.0.4 + dev: false /@typescript-eslint/eslint-plugin/5.30.7_j3fwdls2rzb3mq7xmqckumqsle: resolution: {integrity: sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==} @@ -401,6 +465,10 @@ packages: dependencies: fill-range: 7.0.1 + /buffer-equal-constant-time/1.0.1: + resolution: {integrity: sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=} + dev: false + /bundle-require/3.0.4_esbuild@0.14.49: resolution: {integrity: sha512-VXG6epB1yrLAvWVQpl92qF347/UXmncQj7J3U8kZEbdVZ1ZkQyr4hYeL/9RvcE8vVVdp53dY78Fd/3pqfRqI1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -543,7 +611,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /deep-is/0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -593,6 +660,12 @@ packages: esutils: 2.0.3 dev: true + /ecdsa-sig-formatter/1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /emoji-regex/9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true @@ -1592,6 +1665,13 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true + /jose/2.0.5: + resolution: {integrity: sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==} + engines: {node: '>=10.13.0 < 13 || >=13.7.0'} + dependencies: + '@panva/asn1.js': 1.0.0 + dev: false + /jose/4.8.3: resolution: {integrity: sha512-7rySkpW78d8LBp4YU70Wb7+OTgE3OwAALNVZxhoIhp4Kscp+p/fBkdpxGAMKxvCAMV4QfXBU9m6l9nX/vGwd2g==} dev: false @@ -1627,6 +1707,22 @@ packages: minimist: 1.2.6 dev: true + /jsonwebtoken/8.5.1: + resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} + engines: {node: '>=4', npm: '>=1.4.28'} + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.2 + semver: 5.7.1 + dev: false + /jsx-ast-utils/3.3.2: resolution: {integrity: sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==} engines: {node: '>=4.0'} @@ -1635,6 +1731,35 @@ packages: object.assign: 4.1.2 dev: true + /jwa/1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jwks-rsa/2.1.4: + resolution: {integrity: sha512-mpArfgPkUpX11lNtGxsF/szkasUcbWHGplZl/uFvFO2NuMHmt0dQXIihh0rkPU2yQd5niQtuUHbXnG/WKiXF6Q==} + engines: {node: '>=10 < 13 || >=14'} + dependencies: + '@types/express': 4.17.13 + '@types/jsonwebtoken': 8.5.8 + debug: 4.3.4 + jose: 2.0.5 + limiter: 1.1.5 + lru-memoizer: 2.1.4 + transitivePeerDependencies: + - supports-color + dev: false + + /jws/3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + dev: false + /kleur/4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} @@ -1663,6 +1788,10 @@ packages: engines: {node: '>=10'} dev: true + /limiter/1.1.5: + resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} + dev: false + /lines-and-columns/1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -1680,10 +1809,42 @@ packages: path-exists: 3.0.0 dev: true + /lodash.clonedeep/4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + dev: false + + /lodash.includes/4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: false + + /lodash.isboolean/3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: false + + /lodash.isinteger/4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: false + + /lodash.isnumber/3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: false + + /lodash.isplainobject/4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.isstring/4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: false + /lodash.merge/4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true + /lodash.once/4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false + /lodash.sortby/4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} dev: true @@ -1695,6 +1856,13 @@ packages: js-tokens: 4.0.0 dev: true + /lru-cache/4.0.2: + resolution: {integrity: sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: false + /lru-cache/6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -1702,6 +1870,13 @@ packages: yallist: 4.0.0 dev: true + /lru-memoizer/2.1.4: + resolution: {integrity: sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==} + dependencies: + lodash.clonedeep: 4.5.0 + lru-cache: 4.0.2 + dev: false + /merge-stream/2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true @@ -1743,7 +1918,6 @@ packages: /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /mz/2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -1959,6 +2133,10 @@ packages: react-is: 16.13.1 dev: true + /pseudomap/1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: false + /punycode/2.1.1: resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} engines: {node: '>=6'} @@ -2061,6 +2239,15 @@ packages: mri: 1.2.0 dev: true + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: false + /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true @@ -2420,6 +2607,10 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /yallist/2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: false + /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true From 345238e9eeebedcdd46780e1a7cceef12ba96f10 Mon Sep 17 00:00:00 2001 From: Krzysztof Wolski Date: Wed, 3 Aug 2022 20:51:37 +0200 Subject: [PATCH 2/4] Add withJWTVerified middleware --- src/middleware.ts | 90 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/src/middleware.ts b/src/middleware.ts index 6162580..a32a4ef 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,6 +1,8 @@ import crypto from "crypto"; import * as jose from "jose"; -import type { Middleware } from "retes"; +import jwt, { JwtPayload } from "jsonwebtoken"; +import jwks, { CertSigningKey, RsaSigningKey } from "jwks-rsa"; +import type { Middleware, Request } from "retes"; import { Response } from "retes/response"; import { SALEOR_DOMAIN_HEADER, SALEOR_EVENT_HEADER } from "./const"; @@ -33,7 +35,6 @@ export const withSaleorEventMatch = (handler) => async (request) => { const receivedEvent = request.headers[SALEOR_EVENT_HEADER]; - if (receivedEvent !== expectedEvent) { return Response.BadRequest({ success: false, @@ -90,12 +91,12 @@ export const withWebhookSignatureVerified = signature, }; - const jwks = jose.createRemoteJWKSet( + const remoteJwks = jose.createRemoteJWKSet( new URL(jwksUrl(saleorDomain)) ) as jose.FlattenedVerifyGetKey; try { - await jose.flattenedVerify(jws, jwks); + await jose.flattenedVerify(jws, remoteJwks); } catch { return Response.BadRequest({ success: false, @@ -106,3 +107,84 @@ export const withWebhookSignatureVerified = return handler(request); }; + +export interface DashboardTokenPayload extends JwtPayload { + app: string; +} + +export const withJWTVerified = + (getAppId: (request: Request) => Promise): Middleware => + (handler) => + async (request) => { + const { [SALEOR_DOMAIN_HEADER]: saleorDomain, "authorization-bearer": token } = request.headers; + + if (token === undefined) { + return Response.BadRequest({ + success: false, + message: "Missing token.", + }); + } + + let tokenClaims; + try { + tokenClaims = jwt.decode(token as string); + } catch (e) { + console.error(e); + return Response.BadRequest({ + success: false, + message: "Invalid token.", + }); + } + + if (tokenClaims === null) { + return Response.BadRequest({ + success: false, + message: "Invalid token.", + }); + } + + if ((tokenClaims as DashboardTokenPayload).iss !== saleorDomain) { + return Response.BadRequest({ + success: false, + message: "Invalid token.", + }); + } + + let appId: string | undefined; + try { + appId = await getAppId(request); + } catch (error) { + console.error("Error during getting the app ID."); + console.error(error); + return Response.BadRequest({ + success: false, + message: "Error during token invalidation - could not obtain the app ID.", + }); + } + + if (!appId || (tokenClaims as DashboardTokenPayload).app !== appId) { + return Response.BadRequest({ + success: false, + message: "Invalid token.", + }); + } + + const jwksClient = jwks({ + jwksUri: `https://${saleorDomain}/.well-known/jwks.json`, + }); + const signingKey = await jwksClient.getSigningKey(); + const signingSecret = + (signingKey as CertSigningKey).publicKey || (signingKey as RsaSigningKey).rsaPublicKey; + + try { + jwt.verify(token as string, signingSecret); + } catch (e) { + console.error(e); + return Response.BadRequest({ + success: false, + message: "Invalid token.", + }); + } + + return handler(request); + }; From e203803505c67efc4a842f4f511d508151061024 Mon Sep 17 00:00:00 2001 From: Krzysztof Wolski Date: Thu, 4 Aug 2022 18:36:59 +0200 Subject: [PATCH 3/4] Add saleor headers util --- package.json | 3 - pnpm-lock.yaml | 197 +------------------------------------------------ src/const.ts | 2 + src/headers.ts | 13 ++++ 4 files changed, 18 insertions(+), 197 deletions(-) create mode 100644 src/headers.ts diff --git a/package.json b/package.json index e9dc7c4..c732dd4 100644 --- a/package.json +++ b/package.json @@ -20,12 +20,9 @@ "fast-glob": "^3.2.11", "graphql": "^16.5.0", "jose": "^4.8.3", - "jsonwebtoken": "^8.5.1", - "jwks-rsa": "^2.1.4", "retes": "^0.29.4" }, "devDependencies": { - "@types/jsonwebtoken": "^8.5.8", "@types/node": "^18.0.4", "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.30.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f257ecb..19569c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,7 +1,6 @@ lockfileVersion: 5.4 specifiers: - '@types/jsonwebtoken': ^8.5.8 '@types/node': ^18.0.4 '@typescript-eslint/eslint-plugin': ^5.17.0 '@typescript-eslint/parser': ^5.30.7 @@ -19,8 +18,6 @@ specifiers: graphql: ^16.5.0 husky: ^8.0.1 jose: ^4.8.3 - jsonwebtoken: ^8.5.1 - jwks-rsa: ^2.1.4 prettier: 2.6.2 retes: ^0.29.4 tsm: ^2.2.1 @@ -33,12 +30,9 @@ dependencies: fast-glob: 3.2.11 graphql: 16.5.0 jose: 4.8.3 - jsonwebtoken: 8.5.1 - jwks-rsa: 2.1.4 retes: 0.29.4 devDependencies: - '@types/jsonwebtoken': 8.5.8 '@types/node': 18.0.4 '@typescript-eslint/eslint-plugin': 5.30.7_j3fwdls2rzb3mq7xmqckumqsle '@typescript-eslint/parser': 5.30.7_4hx5bygx4rxgd7xwyndf6ymwce @@ -127,11 +121,6 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.13.0 - /@panva/asn1.js/1.0.0: - resolution: {integrity: sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==} - engines: {node: '>=10.13.0'} - dev: false - /@pkgr/utils/2.3.0: resolution: {integrity: sha512-7dIJ9CRVzBnqyEl7diUHPUFJf/oty2SeoVzcMocc5PeOUDK9KGzvgIBjGRRzzlRDaOjh3ADwH0WeibQvi3ls2Q==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -144,36 +133,6 @@ packages: tslib: 2.4.0 dev: true - /@types/body-parser/1.19.2: - resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} - dependencies: - '@types/connect': 3.4.35 - '@types/node': 18.0.4 - dev: false - - /@types/connect/3.4.35: - resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} - dependencies: - '@types/node': 18.0.4 - dev: false - - /@types/express-serve-static-core/4.17.30: - resolution: {integrity: sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==} - dependencies: - '@types/node': 18.0.4 - '@types/qs': 6.9.7 - '@types/range-parser': 1.2.4 - dev: false - - /@types/express/4.17.13: - resolution: {integrity: sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==} - dependencies: - '@types/body-parser': 1.19.2 - '@types/express-serve-static-core': 4.17.30 - '@types/qs': 6.9.7 - '@types/serve-static': 1.13.10 - dev: false - /@types/json-schema/7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true @@ -182,32 +141,9 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/jsonwebtoken/8.5.8: - resolution: {integrity: sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==} - dependencies: - '@types/node': 18.0.4 - - /@types/mime/1.3.2: - resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} - dev: false - /@types/node/18.0.4: resolution: {integrity: sha512-M0+G6V0Y4YV8cqzHssZpaNCqvYwlCiulmm0PwpNLF55r/+cT8Ol42CHRU1SEaYFH2rTwiiE1aYg/2g2rrtGdPA==} - - /@types/qs/6.9.7: - resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} - dev: false - - /@types/range-parser/1.2.4: - resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} - dev: false - - /@types/serve-static/1.13.10: - resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==} - dependencies: - '@types/mime': 1.3.2 - '@types/node': 18.0.4 - dev: false + dev: true /@typescript-eslint/eslint-plugin/5.30.7_j3fwdls2rzb3mq7xmqckumqsle: resolution: {integrity: sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==} @@ -465,10 +401,6 @@ packages: dependencies: fill-range: 7.0.1 - /buffer-equal-constant-time/1.0.1: - resolution: {integrity: sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=} - dev: false - /bundle-require/3.0.4_esbuild@0.14.49: resolution: {integrity: sha512-VXG6epB1yrLAvWVQpl92qF347/UXmncQj7J3U8kZEbdVZ1ZkQyr4hYeL/9RvcE8vVVdp53dY78Fd/3pqfRqI1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -611,6 +543,7 @@ packages: optional: true dependencies: ms: 2.1.2 + dev: true /deep-is/0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -660,12 +593,6 @@ packages: esutils: 2.0.3 dev: true - /ecdsa-sig-formatter/1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - dependencies: - safe-buffer: 5.2.1 - dev: false - /emoji-regex/9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true @@ -1665,13 +1592,6 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /jose/2.0.5: - resolution: {integrity: sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==} - engines: {node: '>=10.13.0 < 13 || >=13.7.0'} - dependencies: - '@panva/asn1.js': 1.0.0 - dev: false - /jose/4.8.3: resolution: {integrity: sha512-7rySkpW78d8LBp4YU70Wb7+OTgE3OwAALNVZxhoIhp4Kscp+p/fBkdpxGAMKxvCAMV4QfXBU9m6l9nX/vGwd2g==} dev: false @@ -1707,22 +1627,6 @@ packages: minimist: 1.2.6 dev: true - /jsonwebtoken/8.5.1: - resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} - engines: {node: '>=4', npm: '>=1.4.28'} - dependencies: - jws: 3.2.2 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.2 - semver: 5.7.1 - dev: false - /jsx-ast-utils/3.3.2: resolution: {integrity: sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==} engines: {node: '>=4.0'} @@ -1731,35 +1635,6 @@ packages: object.assign: 4.1.2 dev: true - /jwa/1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - dev: false - - /jwks-rsa/2.1.4: - resolution: {integrity: sha512-mpArfgPkUpX11lNtGxsF/szkasUcbWHGplZl/uFvFO2NuMHmt0dQXIihh0rkPU2yQd5niQtuUHbXnG/WKiXF6Q==} - engines: {node: '>=10 < 13 || >=14'} - dependencies: - '@types/express': 4.17.13 - '@types/jsonwebtoken': 8.5.8 - debug: 4.3.4 - jose: 2.0.5 - limiter: 1.1.5 - lru-memoizer: 2.1.4 - transitivePeerDependencies: - - supports-color - dev: false - - /jws/3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} - dependencies: - jwa: 1.4.1 - safe-buffer: 5.2.1 - dev: false - /kleur/4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} @@ -1788,10 +1663,6 @@ packages: engines: {node: '>=10'} dev: true - /limiter/1.1.5: - resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} - dev: false - /lines-and-columns/1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -1809,42 +1680,10 @@ packages: path-exists: 3.0.0 dev: true - /lodash.clonedeep/4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - dev: false - - /lodash.includes/4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - dev: false - - /lodash.isboolean/3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - dev: false - - /lodash.isinteger/4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - dev: false - - /lodash.isnumber/3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - dev: false - - /lodash.isplainobject/4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - dev: false - - /lodash.isstring/4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - dev: false - /lodash.merge/4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true - /lodash.once/4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - dev: false - /lodash.sortby/4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} dev: true @@ -1856,13 +1695,6 @@ packages: js-tokens: 4.0.0 dev: true - /lru-cache/4.0.2: - resolution: {integrity: sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==} - dependencies: - pseudomap: 1.0.2 - yallist: 2.1.2 - dev: false - /lru-cache/6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -1870,13 +1702,6 @@ packages: yallist: 4.0.0 dev: true - /lru-memoizer/2.1.4: - resolution: {integrity: sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==} - dependencies: - lodash.clonedeep: 4.5.0 - lru-cache: 4.0.2 - dev: false - /merge-stream/2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true @@ -1918,6 +1743,7 @@ packages: /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true /mz/2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -2133,10 +1959,6 @@ packages: react-is: 16.13.1 dev: true - /pseudomap/1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - dev: false - /punycode/2.1.1: resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} engines: {node: '>=6'} @@ -2239,15 +2061,6 @@ packages: mri: 1.2.0 dev: true - /safe-buffer/5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false - - /semver/5.7.1: - resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} - hasBin: true - dev: false - /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true @@ -2607,10 +2420,6 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /yallist/2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - dev: false - /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true diff --git a/src/const.ts b/src/const.ts index 36d4c92..35c25bc 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1,2 +1,4 @@ export const SALEOR_DOMAIN_HEADER = "saleor-domain"; export const SALEOR_EVENT_HEADER = "saleor-event"; +export const SALEOR_SIGNATURE_HEADER = "saleor-signature"; +export const SALEOR_AUTHORIZATION_BEARER_HEADER = "authorization-bearer"; diff --git a/src/headers.ts b/src/headers.ts new file mode 100644 index 0000000..fa43f3a --- /dev/null +++ b/src/headers.ts @@ -0,0 +1,13 @@ +import { + SALEOR_AUTHORIZATION_BEARER_HEADER, + SALEOR_DOMAIN_HEADER, + SALEOR_EVENT_HEADER, + SALEOR_SIGNATURE_HEADER, +} from "./const"; + +export const getSaleorHeaders = (headers: { [name: string]: any }) => ({ + domain: headers[SALEOR_DOMAIN_HEADER], + authorizationBearer: headers[SALEOR_AUTHORIZATION_BEARER_HEADER], + signature: headers[SALEOR_SIGNATURE_HEADER], + event: headers[SALEOR_EVENT_HEADER], +}); From fe2921dcdcf8e5e9cc38658ff348a4883cc6474f Mon Sep 17 00:00:00 2001 From: Krzysztof Wolski Date: Thu, 4 Aug 2022 18:37:31 +0200 Subject: [PATCH 4/4] Migrate to Jose and better error messages --- src/middleware.ts | 81 ++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/src/middleware.ts b/src/middleware.ts index a32a4ef..d454028 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,11 +1,10 @@ import crypto from "crypto"; import * as jose from "jose"; -import jwt, { JwtPayload } from "jsonwebtoken"; -import jwks, { CertSigningKey, RsaSigningKey } from "jwks-rsa"; import type { Middleware, Request } from "retes"; import { Response } from "retes/response"; -import { SALEOR_DOMAIN_HEADER, SALEOR_EVENT_HEADER } from "./const"; +import { SALEOR_AUTHORIZATION_BEARER_HEADER } from "./const"; +import { getSaleorHeaders } from "./headers"; import { jwksUrl } from "./urls"; export const withBaseURL: Middleware = (handler) => async (request) => { @@ -18,9 +17,9 @@ export const withBaseURL: Middleware = (handler) => async (request) => { }; export const withSaleorDomainPresent: Middleware = (handler) => async (request) => { - const saleorDomain = request.headers[SALEOR_DOMAIN_HEADER]; + const { domain } = getSaleorHeaders(request.headers); - if (!saleorDomain) { + if (!domain) { return Response.BadRequest({ success: false, message: "Missing Saleor domain header.", @@ -34,11 +33,12 @@ export const withSaleorEventMatch = (expectedEvent: `${Lowercase}`): Middleware => (handler) => async (request) => { - const receivedEvent = request.headers[SALEOR_EVENT_HEADER]; - if (receivedEvent !== expectedEvent) { + const { event } = getSaleorHeaders(request.headers); + + if (event !== expectedEvent) { return Response.BadRequest({ success: false, - message: "Invalid Saleor Event", + message: `Invalid Saleor event. Expecting ${expectedEvent}.`, }); } @@ -61,15 +61,16 @@ export const withWebhookSignatureVerified = (secretKey: string | undefined = undefined): Middleware => (handler) => async (request) => { + const ERROR_MESSAGE = "Webhook signature verification failed:"; + if (request.rawBody === undefined) { return Response.InternalServerError({ success: false, - message: "Request payload already parsed.", + message: `${ERROR_MESSAGE} Request payload already parsed.`, }); } - const { [SALEOR_DOMAIN_HEADER]: saleorDomain, "saleor-signature": payloadSignature } = - request.headers; + const { domain: saleorDomain, signature: payloadSignature } = getSaleorHeaders(request.headers); if (secretKey !== undefined) { const calculatedSignature = crypto @@ -80,7 +81,7 @@ export const withWebhookSignatureVerified = if (calculatedSignature !== payloadSignature) { return Response.BadRequest({ success: false, - message: "Invalid signature.", + message: `${ERROR_MESSAGE} Verification using secret key has failed.`, }); } } else { @@ -100,7 +101,7 @@ export const withWebhookSignatureVerified = } catch { return Response.BadRequest({ success: false, - message: "Invalid signature.", + message: `${ERROR_MESSAGE} Verification using public key has failed.`, }); } } @@ -108,7 +109,7 @@ export const withWebhookSignatureVerified = return handler(request); }; -export interface DashboardTokenPayload extends JwtPayload { +export interface DashboardTokenPayload extends jose.JWTPayload { app: string; } @@ -116,37 +117,30 @@ export const withJWTVerified = (getAppId: (request: Request) => Promise): Middleware => (handler) => async (request) => { - const { [SALEOR_DOMAIN_HEADER]: saleorDomain, "authorization-bearer": token } = request.headers; + const { domain, authorizationBearer: token } = getSaleorHeaders(request.headers); + const ERROR_MESSAGE = "JWT verification failed:"; if (token === undefined) { return Response.BadRequest({ success: false, - message: "Missing token.", + message: `${ERROR_MESSAGE} Missing ${SALEOR_AUTHORIZATION_BEARER_HEADER} header.`, }); } - let tokenClaims; + let tokenClaims: DashboardTokenPayload; try { - tokenClaims = jwt.decode(token as string); + tokenClaims = jose.decodeJwt(token as string) as DashboardTokenPayload; } catch (e) { - console.error(e); return Response.BadRequest({ success: false, - message: "Invalid token.", + message: `${ERROR_MESSAGE} Could not decode authorization token.`, }); } - if (tokenClaims === null) { + if (tokenClaims.iss !== domain) { return Response.BadRequest({ success: false, - message: "Invalid token.", - }); - } - - if ((tokenClaims as DashboardTokenPayload).iss !== saleorDomain) { - return Response.BadRequest({ - success: false, - message: "Invalid token.", + message: `${ERROR_MESSAGE} Token iss property is different than domain header.`, }); } @@ -154,35 +148,34 @@ export const withJWTVerified = try { appId = await getAppId(request); } catch (error) { - console.error("Error during getting the app ID."); - console.error(error); - return Response.BadRequest({ + return Response.InternalServerError({ success: false, - message: "Error during token invalidation - could not obtain the app ID.", + message: `${ERROR_MESSAGE} Could not obtain the app ID.`, }); } - if (!appId || (tokenClaims as DashboardTokenPayload).app !== appId) { - return Response.BadRequest({ + if (!appId) { + return Response.InternalServerError({ success: false, - message: "Invalid token.", + message: `${ERROR_MESSAGE} No value for app ID.`, }); } - const jwksClient = jwks({ - jwksUri: `https://${saleorDomain}/.well-known/jwks.json`, - }); - const signingKey = await jwksClient.getSigningKey(); - const signingSecret = - (signingKey as CertSigningKey).publicKey || (signingKey as RsaSigningKey).rsaPublicKey; + if (tokenClaims.app !== appId) { + return Response.BadRequest({ + success: false, + message: `${ERROR_MESSAGE} Token's app property is different than app ID.`, + }); + } try { - jwt.verify(token as string, signingSecret); + const JWKS = jose.createRemoteJWKSet(new URL(jwksUrl(domain))); + await jose.jwtVerify(token, JWKS); } catch (e) { console.error(e); return Response.BadRequest({ success: false, - message: "Invalid token.", + message: `${ERROR_MESSAGE} JWT signature verification failed.`, }); }