2022-11-28 09:04:26 +00:00
|
|
|
import * as jose from "jose";
|
|
|
|
|
|
|
|
import { createDebug } from "./debug";
|
2023-01-25 11:00:22 +00:00
|
|
|
import { hasPermissionsInJwtToken } from "./has-permissions-in-jwt-token";
|
2023-03-16 14:03:35 +00:00
|
|
|
import { Permission } from "./types";
|
2023-01-11 15:55:10 +00:00
|
|
|
import { getJwksUrlFromSaleorApiUrl } from "./urls";
|
2023-04-26 12:09:50 +00:00
|
|
|
import { verifyTokenExpiration } from "./verify-token-expiration";
|
2022-11-28 09:04:26 +00:00
|
|
|
|
|
|
|
const debug = createDebug("verify-jwt");
|
|
|
|
|
|
|
|
export interface DashboardTokenPayload extends jose.JWTPayload {
|
|
|
|
app: string;
|
2023-03-16 14:03:35 +00:00
|
|
|
user_permissions: Permission[];
|
2022-11-28 09:04:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface verifyJWTArguments {
|
|
|
|
appId: string;
|
2023-01-12 12:39:49 +00:00
|
|
|
saleorApiUrl: string;
|
2022-11-28 09:04:26 +00:00
|
|
|
token: string;
|
2023-03-16 14:03:35 +00:00
|
|
|
requiredPermissions?: Permission[];
|
2022-11-28 09:04:26 +00:00
|
|
|
}
|
|
|
|
|
2023-01-25 11:00:22 +00:00
|
|
|
export const verifyJWT = async ({
|
|
|
|
saleorApiUrl,
|
|
|
|
token,
|
|
|
|
appId,
|
|
|
|
requiredPermissions,
|
|
|
|
}: verifyJWTArguments) => {
|
2022-11-28 09:04:26 +00:00
|
|
|
let tokenClaims: DashboardTokenPayload;
|
|
|
|
const ERROR_MESSAGE = "JWT verification failed:";
|
|
|
|
|
|
|
|
try {
|
|
|
|
tokenClaims = jose.decodeJwt(token as string) as DashboardTokenPayload;
|
|
|
|
debug("Token Claims decoded from jwt");
|
|
|
|
} catch (e) {
|
|
|
|
debug("Token Claims could not be decoded from JWT, will respond with Bad Request");
|
|
|
|
throw new Error(`${ERROR_MESSAGE} Could not decode authorization token.`);
|
|
|
|
}
|
|
|
|
|
2023-04-26 12:09:50 +00:00
|
|
|
try {
|
|
|
|
verifyTokenExpiration(tokenClaims);
|
|
|
|
} catch (e) {
|
|
|
|
throw new Error(`${ERROR_MESSAGE} ${(e as Error).message}`);
|
|
|
|
}
|
|
|
|
|
2022-11-28 09:04:26 +00:00
|
|
|
if (tokenClaims.app !== appId) {
|
|
|
|
debug(
|
|
|
|
"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.`);
|
|
|
|
}
|
|
|
|
|
2023-01-25 11:00:22 +00:00
|
|
|
if (!hasPermissionsInJwtToken(tokenClaims, requiredPermissions)) {
|
|
|
|
debug("Token did not meet requirements for permissions: %s", requiredPermissions);
|
|
|
|
throw new Error(`${ERROR_MESSAGE} Token's permissions are not sufficient.`);
|
|
|
|
}
|
|
|
|
|
2022-11-28 09:04:26 +00:00
|
|
|
try {
|
|
|
|
debug("Trying to create JWKS");
|
|
|
|
|
2023-01-12 12:39:49 +00:00
|
|
|
const JWKS = jose.createRemoteJWKSet(new URL(getJwksUrlFromSaleorApiUrl(saleorApiUrl)));
|
2022-11-28 09:04:26 +00:00
|
|
|
debug("Trying to compare JWKS with token");
|
|
|
|
await jose.jwtVerify(token, JWKS);
|
|
|
|
} catch (e) {
|
|
|
|
debug("Failure: %s", e);
|
|
|
|
debug("Will return with Bad Request");
|
|
|
|
|
|
|
|
console.error(e);
|
|
|
|
|
|
|
|
throw new Error(`${ERROR_MESSAGE} JWT signature verification failed.`);
|
|
|
|
}
|
|
|
|
};
|