Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with jsonwebtokens via @apple/app-store-server-library: "alg" parameter "ES256" requires curve "prime256v1" #22879

Open
jareddr opened this issue Mar 13, 2024 · 2 comments
Assignees
Labels
bug Something isn't working node API polyfill Related to various "node:*" modules APIs node compat

Comments

@jareddr
Copy link

jareddr commented Mar 13, 2024

Version: deno 1.41.2 (release, x86_64-unknown-linux-gnu)
v8 12.1.285.27
typescript 5.3.3

Hi,

I'm attempting to build out the backend of an IOS app on a Supabase edge function want to be able to verify the purchase of in-app purchases using an edge function. I have a block of code interacting with the Apple api working in nodejs on my local machine,

When I attempt to bundle the working code up and run it with Deno I'm running into some runtime errors within the dependencies.

I've tried the import using the npm: prefix like so
import pkg from "npm:@apple/app-store-server-library";
const { AppStoreServerAPIClient, Environment, SignedDataVerifier } = pkg;

And when i attempt to run my code this way I get this error:

Error: "alg" parameter "ES256" requires curve "prime256v1".
at module.exports (file:///tmp/sb-compile-edge-runtime/node_modules/localhost/jsonwebtoken/9.0.2/lib/validateAsymmetricKey.js:46:15)
at module.exports [as sign] (file:///tmp/sb-compile-edge-runtime/node_modules/localhost/jsonwebtoken/9.0.2/sign.js:179:7)
at AppStoreServerAPIClient.createBearerToken (file:///tmp/sb-compile-edge-runtime/node_modules/localhost/@apple/app-store-server-library/1.0.1/dist/index.js:323:29)
at AppStoreServerAPIClient.makeRequest (file:///tmp/sb-compile-edge-runtime/node_modules/localhost/@apple/app-store-server-library/1.0.1/dist/index.js:93:47)
at AppStoreServerAPIClient.getTransactionInfo (file:///tmp/sb-compile-edge-runtime/node_modules/localhost/@apple/app-store-server-library/1.0.1/dist/index.js:285:27)
at getTransactionInformation (file:///home/deno/functions/_shared/apple-helpers.ts:49:31)
at eventLoopTick (ext:core/01_core.js:64:7)
at async Object.handler (file:///home/deno/functions/apple-pay-test/index.ts:14:16)
at async handleHttp (ext:sb_core_main_js/js/http.js:102:17)

I've also tried using esm.sh like so:
import pkg from "https://esm.sh/@apple/app-store-server-library";
const { AppStoreServerAPIClient, Environment, SignedDataVerifier } = pkg;

which results in a different error:
TypeError: Right-hand side of 'instanceof' is not an object
at de.exports [as sign] (https://esm.sh/v135/jsonwebtoken@9.0.2/esnext/jsonwebtoken.mjs:21:10119)
at e.createBearerToken (https://esm.sh/v135/@apple/app-store-server-library@1.0.1/esnext/app-store-server-library.mjs:14:44515)
at e.makeRequest (https://esm.sh/v135/@apple/app-store-server-library@1.0.1/esnext/app-store-server-library.mjs:14:41417)
at e.getTransactionInfo (https://esm.sh/v135/@apple/app-store-server-library@1.0.1/esnext/app-store-server-library.mjs:14:43969)
at getTransactionInformation (file:///home/deno/functions/_shared/apple-helpers.ts:49:31)
at eventLoopTick (ext:core/01_core.js:64:7)
at async Object.handler (file:///home/deno/functions/apple-pay-test/index.ts:14:16)
at async handleHttp (ext:sb_core_main_js/js/http.js:102:17)

I'm able to reproduce the problem the first error locally using the deno command line utility. I've got it hooked up to a debugger and I've pinpointed the resulting issue but still can't figure out the underlying cause or how to fix it.

The error "alg" parameter "ES256" requires curve "prime256v1", comes from the validateAsymmetricKey.js file within the jsonwebtoken module. Specifically this block of code

  if (ASYMMETRIC_KEY_DETAILS_SUPPORTED) {
    switch (keyType) {
    case 'ec':
      const keyCurve = key.asymmetricKeyDetails.namedCurve;
      const allowedCurve = allowedCurves[algorithm];

      if (keyCurve !== allowedCurve) {
        debugger;
        console.log('heya')
        throw new Error(`"alg" parameter "${algorithm}" requires curve "${allowedCurve}".`);
      }
      break;

When I run my code with Deno the value of keyCurve above is 'p256'. Assuming from the text of the error, this needs to be 'prime256v1'.

To find out where the 'p256' value was coming from I followed the debugger into ext:deno_node/internal/crypto/keys.ts which has this function

export function createPrivateKey(key) {
  const { data, format, type } = prepareAsymmetricKey(key);
  const details = op_node_create_private_key(data, format, type);
  const handle = setOwnedKey(copyBuffer(data));
  return new PrivateKeyObject(handle, details);
}

The op_node_create_private_key function is the first instance of the string 'p256' I can see appearing in my program, but I can't step into it.

Searching further I found the 'p256' string inside of the deno crypto module here:

ID_SECP256R1_OID => "p256",

I have no idea what's going on in these packages so I don't know if its a string value problem or it's missing an actual algorithm under the hood. I'd really love to be able to get this working so I don't have to spin up another piece of infrastructure to validate in-app purchases.

If anyone could point me in the right direction for debugging further, I'd be much appreciated.

It's not possible to post my code because it requires a number of private pieces of information from my apple account in order to run.

@jareddr
Copy link
Author

jareddr commented Mar 13, 2024

Seconds after posting this I managed to search the issues list for prime256v1 and found this similar issue: #21761, apologies.

@jareddr
Copy link
Author

jareddr commented Mar 14, 2024

I've distilled my problem down to a simple use of the jsonwebtoken.sign function so it's easier to reproduce.

First, unworking Deno code:

import * as jsonwebtoken from "https://esm.sh/jsonwebtoken@9.0.2";

const payload = { bid: "test" };
const keyId = "test";
const issuerId = "test";

const signingKey =  Deno.readFileSync("apple.p8");
const signedJWT = await jsonwebtoken.sign(payload, signingKey, {
  algorithm: "ES256",
  keyid: keyId,
  issuer: issuerId,
  audience: "appstoreconnect-v1",
  expiresIn: "5m",
});

Results in

deno run --allow-read --allow-net deno-jsonwebtoken-test.js 
error: Uncaught (in promise) Error: secretOrPrivateKey must be an asymmetric key when using ES256
    at Module.de.exports (https://esm.sh/v135/jsonwebtoken@9.0.2/denonext/jsonwebtoken.mjs:21:10470)

Working code in nodejs:

import jsonwebtoken from "jsonwebtoken";
import fs from "fs";

const payload = { bid: "test" };
const keyId = "test";
const issuerId = "test";

const signingKey =  fs.readFileSync("apple.p8");
const signedJWT = await jsonwebtoken.sign(payload, signingKey, {
  algorithm: "ES256",
  keyid: keyId,
  issuer: issuerId,
  audience: "appstoreconnect-v1",
  expiresIn: "5m",
});

To test this, put any contents into the apple.p8 file.
apple.p8

-----BEGIN PRIVATE KEY-----
test key
-----END PRIVATE KEY-----

The Deno version will fail no matter the contents. The Node version will complain that the key is not an asymetric key if you don't specify a valid key. If I send it a valid asymetric key, I'll get back a signed JWT.

These tests were run using the latest version

$ deno --version
deno 1.41.3 (release, x86_64-unknown-linux-gnu)
v8 12.3.219.9
typescript 5.3.3

@bartlomieju bartlomieju added bug Something isn't working node compat node API polyfill Related to various "node:*" modules APIs labels Mar 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working node API polyfill Related to various "node:*" modules APIs node compat
Projects
None yet
Development

No branches or pull requests

3 participants