UNPKG

@saleor/app-sdk

Version:
130 lines (123 loc) 4.09 kB
import { createDebug } from "./chunk-CPDLIPGD.mjs"; // src/auth/verify-jwt.ts import * as jose from "jose"; // src/auth/has-permissions-in-jwt-token.ts var debug = createDebug("checkJwtPermissions"); var hasPermissionsInJwtToken = (tokenData, permissionsToCheckAgainst) => { debug(`Permissions required ${permissionsToCheckAgainst}`); if (!permissionsToCheckAgainst?.length) { debug("No permissions specified, check passed"); return true; } const userPermissions = tokenData?.user_permissions || void 0; if (!userPermissions?.length) { debug("User has no permissions assigned. Rejected"); return false; } const arePermissionsSatisfied = permissionsToCheckAgainst.every( (permission) => userPermissions.includes(permission) ); if (!arePermissionsSatisfied) { debug("Permissions check not passed"); return false; } debug("Permissions check successful"); return true; }; // src/auth/verify-token-expiration.ts var debug2 = createDebug("verify-token-expiration"); var verifyTokenExpiration = (token) => { const tokenExpiration = token.exp; const now = /* @__PURE__ */ new Date(); const nowTimestamp = now.valueOf(); if (!tokenExpiration) { throw new Error('Missing "exp" field in token'); } const tokenMsTimestamp = tokenExpiration * 1e3; debug2( "Comparing to days date: %s and token expiration date: %s", now.toLocaleString(), new Date(tokenMsTimestamp).toLocaleString() ); if (tokenMsTimestamp <= nowTimestamp) { throw new Error("Token is expired"); } }; // src/auth/verify-jwt.ts var debug3 = createDebug("verify-jwt"); var verifyJWT = async ({ saleorApiUrl, token, appId, requiredPermissions }) => { let tokenClaims; const ERROR_MESSAGE = "JWT verification failed:"; try { tokenClaims = jose.decodeJwt(token); debug3("Token Claims decoded from jwt"); } catch (e) { debug3("Token Claims could not be decoded from JWT, will respond with Bad Request"); throw new Error(`${ERROR_MESSAGE} Could not decode authorization token.`); } try { verifyTokenExpiration(tokenClaims); } catch (e) { throw new Error(`${ERROR_MESSAGE} ${e.message}`); } if (tokenClaims.app !== appId) { debug3( "Resolved App ID value from token to be different than in request, will respond with Bad Request" ); throw new Error(`${ERROR_MESSAGE} Token's app property is different than app ID.`); } if (!hasPermissionsInJwtToken(tokenClaims, requiredPermissions)) { debug3("Token did not meet requirements for permissions: %s", requiredPermissions); throw new Error(`${ERROR_MESSAGE} Token's permissions are not sufficient.`); } try { debug3("Trying to create JWKS"); const JWKS = jose.createRemoteJWKSet(new URL(getJwksUrlFromSaleorApiUrl(saleorApiUrl))); debug3("Trying to compare JWKS with token"); await jose.jwtVerify(token, JWKS); } catch (e) { debug3("Failure: %s", e); debug3("Will return with Bad Request"); console.error(e); throw new Error(`${ERROR_MESSAGE} JWT signature verification failed.`); } }; // src/auth/verify-signature.ts import * as jose2 from "jose"; var debug4 = createDebug("verify-signature"); var verifySignatureWithJwks = async (jwks, signature, rawBody) => { const [header, , jwsSignature] = signature.split("."); const jws = { protected: header, payload: rawBody, signature: jwsSignature }; let localJwks; try { const parsedJWKS = JSON.parse(jwks); localJwks = jose2.createLocalJWKSet(parsedJWKS); } catch { debug4("Could not create local JWKSSet from given data: %s", jwks); throw new Error("JWKS verification failed - could not parse given JWKS"); } try { await jose2.flattenedVerify(jws, localJwks); debug4("JWKS verified"); } catch { debug4("JWKS verification failed"); throw new Error("JWKS verification failed"); } }; var getJwksUrlFromSaleorApiUrl = (saleorApiUrl) => `${new URL(saleorApiUrl).origin}/.well-known/jwks.json`; export { verifyJWT, verifySignatureWithJwks, getJwksUrlFromSaleorApiUrl };