deno icon indicating copy to clipboard operation
deno copied to clipboard

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

Open jareddr opened this issue 1 year ago • 11 comments

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/[email protected]/esnext/jsonwebtoken.mjs:21:10119) at e.createBearerToken (https://esm.sh/v135/@apple/[email protected]/esnext/app-store-server-library.mjs:14:44515) at e.makeRequest (https://esm.sh/v135/@apple/[email protected]/esnext/app-store-server-library.mjs:14:41417) at e.getTransactionInfo (https://esm.sh/v135/@apple/[email protected]/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: https://github.com/denoland/deno/blob/c10d96cb21d5f75b4c6f7b7d8d96d12a6edeee99/ext/node/ops/crypto/mod.rs#L1445

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 avatar Mar 13 '24 01:03 jareddr

Seconds after posting this I managed to search the issues list for prime256v1 and found this similar issue: https://github.com/denoland/deno/issues/21761, apologies.

jareddr avatar Mar 13 '24 01:03 jareddr

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/[email protected]";

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/[email protected]/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

jareddr avatar Mar 14 '24 22:03 jareddr

Related to:

  • #21124

birkskyum avatar Jun 23 '24 12:06 birkskyum

@jareddr I'm facing a similar issue implementing apple in app purchases with supabase edge functions. Did you find a way to make it work?

vykes-mac avatar Aug 04 '24 01:08 vykes-mac

I never got it working in Deno. I ended up spinning up a google runcloud instance running nodejs that validates my tokens for me.

I forward the token from my edge function to my cloudrun instance with an http request.

On Sat, Aug 3, 2024 at 6:53 PM vykes-mac @.***> wrote:

@jareddr https://github.com/jareddr I'm facing a similar issue implementing apple in app purchases with supabase edge functions. Did you find a way to make it work?

— Reply to this email directly, view it on GitHub https://github.com/denoland/deno/issues/22879#issuecomment-2267239423, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFHZ467VAY7DENW4BBDZ53ZPWCP3AVCNFSM6AAAAABETIBQCKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRXGIZTSNBSGM . You are receiving this because you were mentioned.Message ID: @.***>

jareddr avatar Aug 04 '24 02:08 jareddr

@jareddr I'm facing a similar issue implementing apple in app purchases with supabase edge functions. Did you find a way to make it work?

Did you get around it @jareddr ? I am facing the exact the same issue.

anuragajwani avatar Sep 07 '24 14:09 anuragajwani

@jareddr I'm facing a similar issue implementing apple in app purchases with supabase edge functions. Did you find a way to make it work?

Did you get around it @jareddr ? I am facing the exact the same issue.

I gave up trying sorry. I ended up spinning up a separate nodejs service that I can forward these requests to from my deno app. It's sad, but it worked.

jareddr avatar Sep 07 '24 14:09 jareddr

@anuragajwani I also didn't get it working, I decided to spin up a nodejs express backend on digitalocean, then call that endpoint from edge function.

vykes-mac avatar Sep 07 '24 14:09 vykes-mac

Came across this issue and did some digging. jsonwebtoken is looking for "prime256v1" but what comes out of Apple's private key is "p256." Looks like the same thing, just abbreviated. ~~Idk where to go from here.~~

Fixed my issue and shared the bulk of it to a gist https://gist.github.com/NetOpWibby/73d7bf536a395de82a4811fb5f119144

NetOpWibby avatar Oct 14 '24 18:10 NetOpWibby

Came across this issue and did some digging. jsonwebtoken is looking for "prime256v1" but what comes out of Apple's private key is "p256." Looks like the same thing, just abbreviated. ~Idk where to go from here.~

Fixed my issue and shared the bulk of it to a gist https://gist.github.com/NetOpWibby/73d7bf536a395de82a4811fb5f119144

So you now have a local version of the @apple/app-store-server-library lib and you've modified it to use your custom code instead of the jsonwebtoken library?

Or is there a way just to override jsonwebtoken with a local version?

jareddr avatar Oct 14 '24 23:10 jareddr

I have a local version of jsonwebtoken, I'm not using that other Apple library.

NetOpWibby avatar Oct 15 '24 00:10 NetOpWibby

It still would be really great if @apple/app-store-server-library was actually compatible with Deno! 🙏

jaskinty avatar Nov 24 '24 00:11 jaskinty

This is still happening in deno 2.2.3, forcing me to use node.js for the push server. I'd love if we could get @apple/app-store-server-library working.

tkafka avatar Mar 11 '25 09:03 tkafka