From b781e81c5123e0a84ab6e1f3b757e33660aa91e9 Mon Sep 17 00:00:00 2001 From: TJ Boyle Date: Fri, 8 Dec 2023 11:48:38 -0500 Subject: [PATCH] attempt with libs and slightly diff verification process --- nextjs/hello-app/app/cognito-debug/page.tsx | 150 ++++++++++++------- nextjs/hello-app/package-lock.json | 155 +++++++++++++++++++- nextjs/hello-app/package.json | 4 + 3 files changed, 251 insertions(+), 58 deletions(-) diff --git a/nextjs/hello-app/app/cognito-debug/page.tsx b/nextjs/hello-app/app/cognito-debug/page.tsx index 8183be5..a75c516 100644 --- a/nextjs/hello-app/app/cognito-debug/page.tsx +++ b/nextjs/hello-app/app/cognito-debug/page.tsx @@ -2,8 +2,14 @@ import { Buffer } from "buffer"; import { headers } from 'next/headers'; import { CognitoJwtVerifier } from "aws-jwt-verify"; import crypto from "crypto"; +import * as jwt from "jsonwebtoken"; +import base64Url from "base64url"; + +const jws = require("jws"); +const fetch = require("node-fetch").default; async function getJwtPayload() { + // https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html // https://github.com/awslabs/aws-jwt-verify @@ -13,66 +19,106 @@ async function getJwtPayload() { console.log(`encodedJwt = ${encodedJwt}`); if (encodedJwt) { const awsRegion = 'us-east-1'; - const jwtEncodedTokenList = encodedJwt.split('.'); - const jwtEncodedHeaders = jwtEncodedTokenList[0]; - const jwtEncodedPayload = jwtEncodedTokenList[1]; - const jwtEncodedSignature = jwtEncodedTokenList[2]; - const jwtDecodedHeaders = Buffer.from(jwtEncodedHeaders, 'base64').toString('binary'); - const jwtPayload = Buffer.from(jwtEncodedPayload, 'base64').toString('binary'); - console.log(`jwtDecodedHeaders = ${jwtDecodedHeaders}`); - console.log(`jwtPayload = ${jwtPayload}`); - - // Step 2: Get the public key from regional endpoint - const jwtKid = JSON.parse(jwtDecodedHeaders).kid; - console.log(`jwtKid = ${jwtKid}`); - const jwtPublicKeyUrl = `https://public-keys.auth.elb.${awsRegion}.amazonaws.com/${jwtKid}`; - const jwtPublicKeyResponse = await fetch(jwtPublicKeyUrl, { method: 'GET' , cache: "no-store"}); - const jwtPublicKey = await jwtPublicKeyResponse.text(); - console.log(`jwtPublicKey = ${jwtPublicKey}`); - - // Step 3: Verify the signature of the JWT using the public key - const verifier = crypto.createVerify('RSA-SHA256'); - verifier.update(`${jwtEncodedHeaders}.${jwtEncodedPayload}`); - const isVerified = verifier.verify(jwtPublicKey, jwtEncodedSignature, 'base64'); - if (isVerified) { - console.log('JWT signature verified.') - // return jwtPayload - return encodedJwt; - } else { - console.log('JWT NOT signature verified via manual verify.') - } - // Verifier not verifying JWT - const cognitoUserPoolId = process.env.COGNITO_USER_POOL_ID; - if (!cognitoUserPoolId) { - console.log('No process.env.COGNITO_USER_POOL_ID found.'); + var base64UrlToken = base64Url.fromBase64(encodedJwt); + const decoded = jwt.decode(base64UrlToken, { complete: true }); + + if (!decoded) { + console.log('jwt could not be decoded') return null; } - const cognitoClientId = process.env.COGNITO_CLIENT_ID; - if (!cognitoClientId) { - console.log('No process.env.COGNITO_CLIENT_ID found.'); - return null; - } - const cognitoVerifier = CognitoJwtVerifier.create({ - userPoolId: cognitoUserPoolId, - tokenUse: null, - clientId: cognitoClientId, - }); + + const { kid } = decoded.header; + + const uri = `https://public-keys.auth.elb.${awsRegion}.amazonaws.com/${kid}`; + + console.log(`Fetching key at: ${uri}`); + + const response = await fetch(uri); + const key = await response.text(); + + console.log(key); + try { - const jwtVerifiedPayload = await cognitoVerifier.verify(encodedJwt, {}); - console.log(`Token is valid. Verified Payload: ${jwtVerifiedPayload}`); - // CognitoJwtPayload not a string - // return jwtVerifiedPayload; - // return jwtPayload; - return encodedJwt; - } catch (e) { - console.log("Token not valid via CognitoJwtVerifier!"); - console.log(e) + const verify = jws.verify(encodedJwt, "ES256", key); + if (!verify) { + console.log('JWT signature not verified.') + + return null; + } + + console.log('JWT signature verified.') + + return encodedJwt + } catch (err) { + console.log('JWT signature not verified due to error.') + console.error(err); + throw err; } + + // // Old + // const awsRegion = 'us-east-1'; + // const jwtEncodedTokenList = encodedJwt.split('.'); + // const jwtEncodedHeaders = jwtEncodedTokenList[0]; + // const jwtEncodedPayload = jwtEncodedTokenList[1]; + // const jwtEncodedSignature = jwtEncodedTokenList[2]; + // const jwtDecodedHeaders = Buffer.from(jwtEncodedHeaders, 'base64').toString('binary'); + // const jwtPayload = Buffer.from(jwtEncodedPayload, 'base64').toString('binary'); + // console.log(`jwtDecodedHeaders = ${jwtDecodedHeaders}`); + // console.log(`jwtPayload = ${jwtPayload}`); + + // // Step 2: Get the public key from regional endpoint + // const jwtKid = JSON.parse(jwtDecodedHeaders).kid; + // console.log(`jwtKid = ${jwtKid}`); + // const jwtPublicKeyUrl = `https://public-keys.auth.elb.${awsRegion}.amazonaws.com/${jwtKid}`; + // const jwtPublicKeyResponse = await fetch(jwtPublicKeyUrl, { method: 'GET' , cache: "no-store"}); + // const jwtPublicKey = await jwtPublicKeyResponse.text(); + // console.log(`jwtPublicKey = ${jwtPublicKey}`); + + // // Step 3: Verify the signature of the JWT using the public key + // const verifier = crypto.createVerify('RSA-SHA256'); + // verifier.update(`${jwtEncodedHeaders}.${jwtEncodedPayload}`); + // const isVerified = verifier.verify(jwtPublicKey, jwtEncodedSignature, 'base64'); + // if (isVerified) { + // console.log('JWT signature verified.') + // // return jwtPayload + // return encodedJwt; + // } else { + // console.log('JWT NOT signature verified via manual verify.') + // } + + // // Verifier not verifying JWT + // const cognitoUserPoolId = process.env.COGNITO_USER_POOL_ID; + // if (!cognitoUserPoolId) { + // console.log('No process.env.COGNITO_USER_POOL_ID found.'); + // return null; + // } + // const cognitoClientId = process.env.COGNITO_CLIENT_ID; + // if (!cognitoClientId) { + // console.log('No process.env.COGNITO_CLIENT_ID found.'); + // return null; + // } + // const cognitoVerifier = CognitoJwtVerifier.create({ + // userPoolId: cognitoUserPoolId, + // tokenUse: null, + // clientId: cognitoClientId, + // }); + // try { + // const jwtVerifiedPayload = await cognitoVerifier.verify(encodedJwt, {}); + // console.log(`Token is valid. Verified Payload: ${jwtVerifiedPayload}`); + // // CognitoJwtPayload not a string + // // return jwtVerifiedPayload; + // // return jwtPayload; + // return encodedJwt; + // } catch (e) { + // console.log("Token not valid via CognitoJwtVerifier!"); + // console.log(e) + // } + // For now return the unverified JWT payload since verifier is not working // return jwtPayload; - return encodedJwt; + // return encodedJwt; } else { console.log('No JWT found.'); return null; diff --git a/nextjs/hello-app/package-lock.json b/nextjs/hello-app/package-lock.json index 515f9bf..7fb23cd 100644 --- a/nextjs/hello-app/package-lock.json +++ b/nextjs/hello-app/package-lock.json @@ -9,11 +9,15 @@ "version": "0.1.0", "dependencies": { "aws-jwt-verify": "^4.0.0", + "base64url": "^3.0.1", + "jsonwebtoken": "^9.0.2", + "jws": "^4.0.0", "next": "14.0.3", "react": "^18", "react-dom": "^18" }, "devDependencies": { + "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -399,6 +403,15 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.10.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", @@ -875,6 +888,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -938,6 +959,11 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -1230,6 +1256,14 @@ "node": ">=6.0.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.603", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.603.tgz", @@ -2699,6 +2733,46 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "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.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -2714,6 +2788,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2784,12 +2877,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2805,7 +2933,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2859,8 +2986,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mz": { "version": "2.7.0", @@ -3653,6 +3779,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -3679,7 +3824,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -4365,8 +4509,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "2.3.4", diff --git a/nextjs/hello-app/package.json b/nextjs/hello-app/package.json index 220b829..2dc72b3 100644 --- a/nextjs/hello-app/package.json +++ b/nextjs/hello-app/package.json @@ -10,11 +10,15 @@ }, "dependencies": { "aws-jwt-verify": "^4.0.0", + "base64url": "^3.0.1", + "jsonwebtoken": "^9.0.2", + "jws": "^4.0.0", "next": "14.0.3", "react": "^18", "react-dom": "^18" }, "devDependencies": { + "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18",