@saleor/app-sdk
Version:
SDK for building great Saleor Apps
130 lines (123 loc) • 4.09 kB
JavaScript
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
};