401 error when accessing API via node.js

I'm trying to access the MusicKit API but it keeps returning a 401 unauthorized error. I can't figure this out. I have this code to generate a Developer Token:


const privateKey = fs.readFileSync("resources/AuthKey.p8").toString();
  const teamId = "MYTEAMID";
  const keyId = "MYKEYID";

  const options = {
    algorithm: "ES256",
    expiresIn: "180d",
    issuer: "MYTEAMID", // your 10-character Team ID, obtained from your developer account
    header: {
      alg: "ES256",
      kid: "MYKEYID", // your MusicKit Key ID
    },
  };

  return new Promise((resolve: any, reject: any) => {
    jwt.sign({}, privateKey, options, (error, token) => {
      if (error) {
        return reject(error);
      } else { // token created
        return resolve(token);
      }
    });
  });



This generates a successful token. When decoded, this token has header the values like:


{
  "alg": "ES256",
  "typ": "JWT",
  "kid": "MYKEYID"
}



and the payload values like:



{
  "iat": 1558197586,
  "exp": 1573749586,
  "iss": "MYTEAMID"
}


I then try to use that in a curl request for a sample artist like `curl -v -H 'Authorization: Bearer <MYTOKEN>' "https://api.music.apple.com/v1/catalog/us/artists/36954"` but it comes back with a 401 error:



Trying 23.13.216.88...
* TCP_NODELAY set
* Connected to api.music.apple.com (23.13.216.88) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: businessCategory=Private Organization; jurisdictionCountryName=US; jurisdictionStateOrProvinceName=California; serialNumber=C0806592; C=US; ST=California; L=Cupertino; O=Apple Inc.; OU=Internet Services for Akamai; CN=itunes.apple.com
*  start date: May  1 00:00:00 2019 GMT
*  expire date: May  1 12:00:00 2020 GMT
*  subjectAltName: host "api.music.apple.com" matched cert's "api.music.apple.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 Extended Validation Server CA
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7ff42a004600)
> GET /v1/catalog/us/artists/36954 HTTP/2
> Host: api.music.apple.com
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjlYNFBRNUI5TkQifQ.eyJpYXQiOjE1NTgxOTcwNzgsImV4cCI6MTU3Mzc0OTA3OCwiaXNzIjoiVzY5REI0NzRTOSJ9.Erd6KRY7BkkMPMuptdMFcmerehRd_k7BEwqFz16lHhxw9qSYwGANNNat37a-hmw-l95p38Vz0n8Znwpu41kIVQ
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 401
< content-type: application/json; charset=utf-8
< access-control-allow-origin: *
< strict-transport-security: max-age=31536000; includeSubDomains
< date: Sat, 18 May 2019 16:33:58 GMT
< x-cache: TCP_MISS from a104-117-183-52.deploy.akamaitechnologies.com (AkamaiGHost/9.6.4.1-25700704) 
<
* Connection #0 to host api.music.apple.com left intact

Why???

Replies

One of the possible issues could be different line endings style of the key file and nodejs environment. Try either changing line endings in the file or put some kind of placeholders instead and then in your program replace it with a proper line break character.

Were you ever able to get this working? I'm also getting a 401.

hi its yusuf this code is working for me ...


$ cat generate_jwt.js
"use strict";


const fs      = require("fs");
const jwt     = require("jsonwebtoken");


const privateKey = fs.readFileSync("AuthKey.p8").toString();
const teamId     = "MYTEAMID";
const keyId      = "MYKEYID";


const jwtToken = jwt.sign({}, privateKey, {
  algorithm: "ES256",
  expiresIn: "180d",
  issuer: teamId,
  header: {
    alg: "ES256",
    kid: keyId
  }
});


console.log(jwtToken);


AuthKey.p8 looks like this for me

shelf $ cat AuthKey.p8
-----BEGIN PRIVATE KEY-----
M...a
-----END PRIVATE KEY-----
shelf $


hope that helps.

I can't get this to work for me,


using

curl -v -H "Authorization: Bearer TOKENHERE" https://api.appstoreconnect.apple.com/v1/salesReports


and getting


"errors": [{

"status": "401",

"code": "NOT_AUTHORIZED",

"title": "Authentication credentials are missing or invalid.",

"detail": "Provide a properly configured and signed bearer token, and make sure that it has not expired. Learn more about Generating Tokens for API Requests https://developer.apple.com/go/?id=api-generating-tokens"

}]


Also, Apple's API documentation says expiresIn value cannot be greater than 20 minutes. I've tried countless variations of the above code in both JS and Python, and I keep getting a 401.

I don't understanding why you're asking this question in a thread about MusicKit. You said:


>> I've tried countless variations of the above code in both JS and Python


All of the "above" code is about generating MusicKit developer tokens, which are constructed differently from App Store Connect tokens.

Because this hits exactly the same API with exactly the same code. And every single NodeJS I'm using throws exactly the same error. Even using a package like "appstoreconnect" which takes just three strings (secret, issuer and key) throws a 401.