Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tangy-rings-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@saleor/app-sdk": major
---

MetadataManager now requires deleteMetadata to be defined
24 changes: 24 additions & 0 deletions src/auth/verify-signature.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, expect, it } from "vitest";

import { verifySignatureWithJwks } from "@/auth/verify-signature";

/**
* Actual signature copied from testing webhook payload.
*/
const testSignature =
"eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImlzcyI6Imh0dHBzOi8vaGFja2F0aG9uLXNoaXBwaW5nLmV1LnNhbGVvci5jbG91ZC9ncmFwaHFsLyIsImtpZCI6InlLdzlYeUVmOHgyYnd5SkdCWVlTckhzeXpHaHhKaktLdlpkb2tOZTlGSWMiLCJ0eXAiOiJKV1QifQ..ShPbfvYc_A5Aq3hiT6sisDclKikDkhxOvGXT2ZWgdsGRjZpg9ukiHRZym0kbfMfDqU5C3Pfo6n7am0ExwbnFWBfOil3pfe3uJOcOn_UGRj76Fy-59TB0JdS_WuTgNQcYM8Yjvlq2sNK4jdAfJVRTTx8FVgEpFrHBKmcMPfD7zuDozswIDMZOkklYqBcyQ76DJYIRVhl3QsktYPPrxDoqf-GJ--e9FuNqtqNDksP1weiDSraqXCF4-Ie7UWZsMIFxkPF8jdKjF_s1UmNS8Xel8soFQQ9L6Gps-NEv7xcHicGt5lgohH4mqhz1YIxCR7v_NTQgWImu_GQ6ELBiBSIZ2Q";

const rawContent = "{\"__typename\": \"OrderCreated\"}";

const jwks =
"{\"keys\": [{\"kty\": \"RSA\", \"key_ops\": [\"verify\"], \"n\": \"uDhbbpspufsQiqHsmC4kvmFQ5l2mGZsGcWhKVSQKQubSdXMedPpLnPD3Z3DsY76DILTm6WfOtSp5rr4KzF5wjurlOEhuFsB1HUfK9ZZB2nEDCQbweoIv3SOdclaNB__pYvQ0nmQHwsAeqH1QUuFUIvOL3t31rhjvzX6wvS49fGNb7rDlqQjufCvaX_n-ADJTgEAg6y1Mzn5NhgoTV1KTBeviyZqCdwvD6bk1ghN2XXWpNcARTzu3WHrmzIzkTwQeIMG8efwIddjfCaMGiOzAfzdQlqHlHPL1Xb5kV9AVX3kiSy-9shaQY23HdWwwiodrb4k2w34Z9ZZN-MHp8i6JdQ\", \"e\": \"AQAB\", \"use\": \"sig\", \"kid\": \"yKw9XyEf8x2bwyJGBYYSrHsyzGhxJjKKvZdokNe9FIc\"}]}";

describe("verifySignatureWithJwks", () => {
it("Returns empty promise if signature is valid", () =>
expect(verifySignatureWithJwks(jwks, testSignature, rawContent)).resolves.not.toThrow());

it("Throws if signature is invalid", () =>
expect(
verifySignatureWithJwks(jwks, testSignature, "{\"forged\": \"payload\"}"),
).rejects.toThrow());
});
6 changes: 2 additions & 4 deletions src/auth/verify-signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ const debug = createDebug("verify-signature");
/**
* Verify the Webhook payload signature from provided JWKS string.
* JWKS can be cached to avoid unnecessary calls.
*
* TODO: Add test
*/
export const verifySignatureWithJwks = async (jwks: string, signature: string, rawBody: string) => {
const [header, , jwsSignature] = signature.split(".");
Expand All @@ -22,14 +20,14 @@ export const verifySignatureWithJwks = async (jwks: string, signature: string, r

try {
const parsedJWKS = JSON.parse(jwks);

localJwks = jose.createLocalJWKSet(parsedJWKS) as jose.FlattenedVerifyGetKey;
} catch {
debug("Could not create local JWKSSet from given data: %s", jwks);

throw new Error("JWKS verification failed - could not parse given JWKS");
}

debug("Created remote JWKS");

try {
await jose.flattenedVerify(jws, localJwks);
debug("JWKS verified");
Expand Down
1 change: 0 additions & 1 deletion src/handlers/shared/saleor-webhook-validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ describe("SaleorWebhookValidator", () => {
});
});

// TODO: This should be required
it("Fallbacks to null if version is missing in payload", async () => {
vi.spyOn(adapter, "getRawBody").mockResolvedValue(JSON.stringify({}));
vi.spyOn(requestProcessor, "getSaleorHeaders").mockReturnValue(validHeaders);
Expand Down
12 changes: 4 additions & 8 deletions src/settings-manager/metadata-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@
export interface MetadataManagerConfig {
fetchMetadata: FetchMetadataCallback;
mutateMetadata: MutateMetadataCallback;
/**
* Keep it optional, to avoid breaking changes. If not provided, delete will throw.
* TODO: Make it required in next major version
*/
deleteMetadata?: DeleteMetadataCallback;
deleteMetadata: DeleteMetadataCallback;
}

/**
Expand All @@ -62,7 +58,7 @@

private mutateMetadata: MutateMetadataCallback;

private deleteMetadata?: DeleteMetadataCallback;
private deleteMetadata: DeleteMetadataCallback;

constructor({ fetchMetadata, mutateMetadata, deleteMetadata }: MetadataManagerConfig) {
this.fetchMetadata = fetchMetadata;
Expand All @@ -89,7 +85,7 @@
}
// changes should update cache
const metadata = await this.mutateMetadata(serializedMetadata);
this.settings = metadata.map(deserializeMetadata);
this.settings = metadata.map(deserializeMetadata);?

Check failure on line 88 in src/settings-manager/metadata-manager.ts

View workflow job for this annotation

GitHub Actions / build

Declaration or statement expected.
}

/**
Expand All @@ -98,7 +94,7 @@
async delete(args: DeleteSettingsValue | DeleteSettingsValue[] | string | string[]) {
if (!this.deleteMetadata) {
throw new Error(
"Delete not implemented. Ensure MetadataManager is configured with deleteMetadata param in constructor"
"Delete not implemented. Ensure MetadataManager is configured with deleteMetadata param in constructor",
);
}

Expand Down
Loading