We're preparing to migrate our Apple Sign-In users for an upcoming app transfer. Following this
guide, we're currently stuck on the first curl
command:
curl -v POST "https://appleid.apple.com/auth/token" \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'scope=user.migration' \
-d 'client_id=CLIENT_ID' \
-d 'client_secret=CLIENT_SECRET_ISSUED_BY_TEAM_A'
Specifically, we're having issues generating the client secret, specified here.
We're using a Node.js script to generate the script; initially I realized that the private key I was signing the JWT with was wrong (I was using the App Store Connect API team key instead of a private key for use with Account & Organization Data Sharing).
Every time we try entering this curl
command:
curl -v POST "https://appleid.apple.com/auth/token" \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'scope=user.migration' \
-d 'client_id=com.jumbofungames.platMaker' \
-d 'client_secret=$(node clientsecret_jwt2.js)'
Where $(node clientsecret_jwt2.js)
is the command to generate the client secret; we get this error:
< HTTP/1.1 400 Bad Request
< Server: Apple
< Date: Mon, 19 Aug 2024 15:41:31 GMT
< Content-Type: application/json;charset=UTF-8
< Content-Length: 49
< Connection: keep-alive
< Pragma: no-cache
< Cache-Control: no-store
<
* Connection #1 to host appleid.apple.com left intact
{"error":"invalid_client","email_verified":false}%
Here is the script we are using to generate the Client Secret (JWT), with some variables using placeholders for privacy:
const fs = require('fs');
const jwt = require('jsonwebtoken'); // npm i jsonwebtoken
// You get privateKey, keyId, and teamId from your Apple Developer account
const privateKey = fs.readFileSync("./AuthKey_ABCDEFG123.p8") // ENTER THE PATH TO THE TEAM KEY HERE (private key file)
const keyId = "API_KEY"; // ENTER YOUR API KEY ID HERE
const teamId = "TEAM_ID"; // ENTER YOUR TEAM ID HERE
const clientId = "com.companyname.appname"; // ENTER YOUR APP ID OR SERVICES ID HERE (This is the client_id)
// Time settings (in seconds)
let now = Math.round((new Date()).getTime() / 1000); // Current time (seconds since epoch)
let nowPlus6Months = now + 15776999; // 6 months from now (maximum expiration time)
let payload = {
"iss": teamId, // The Team ID associated with your developer account
"iat": now, // Issued at time
"exp": nowPlus6Months, // Expiration time (up to 6 months)
"aud": "https://appleid.apple.com", // Audience
"sub": clientId // The App ID or Services ID
}
let signOptions = {
"algorithm": "ES256", // Algorithm for signing (ECDSA with P-256 curve and SHA-256 hash algorithm)
header : {
"alg": "ES256", // Algorithm in the header
"kid": keyId, // Key ID in the header
"typ": "JWT" // JWT type
}
};
// Generate the JWT (client_secret)
let clientSecret = jwt.sign(payload, privateKey, signOptions);
console.log(clientSecret);
module.exports = clientSecret;
If anyone has run into similar issues using this API or could shed some light on what could be going wrong, please let us know — we're at a bit of a loss here.
The issue I was having with my client secret was that I was accessing it incorrectly with my curl command —
I had to change single quotes (``) to double quotes("") to get my client secret script to execute within the curl
command properly:
curl -v POST "https://appleid.apple.com/auth/token"
-H 'Content-Type: application/x-www-form-urlencoded'
-d 'grant_type=client_credentials'
-d 'scope=user.migration'
-d 'client_id=com.jumbofungames.platMaker'
-d "client_secret=$(node clientsecret_jwt2.js)"