I maintain getting 401 Unauthorized error when fetching Apple’s public keys.
In [14]: print(f”Error fetching public keys: {response.status_code} {response.textual content}”)
Error fetching public keys: 401 Unauthenticated
I’ve verified that the Key ID, Issuer ID, and personal key file are all right, with the personal key having admin entry. The server time is accurately set to UTC. Given this, I can not establish what could be inflicting the problem. Any insights?
def generate_apple_developer_token():
# Load the personal key in PEM format
with open(PRIVATE_KEY_FILE, 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.learn(),
password=None,
backend=default_backend()
)
# JWT header
headers = {
"alg": "ES256", # Algorithm: Elliptic Curve
"child": KEY_ID, # Key ID from Apple Developer
"typ": "JWT" # Kind: JWT
}
# JWT payload
payload = {
"iss": ISSUER_ID, # Issuer ID from Apple Developer
"iat": int(datetime.utcnow().timestamp()), # Issued at time
"exp": int((datetime.utcnow() + timedelta(minutes=10)).timestamp()), # Expiration (max 10 minutes)
"aud": "appstoreconnect-v1", # Viewers
}
# Encode the header and payload as base64
header_base64 = base64.urlsafe_b64encode(json.dumps(headers).encode()).decode().rstrip("=")
payload_base64 = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")
# Concatenate header and payload
message = f"{header_base64}.{payload_base64}".encode()
# Signal the message utilizing ECDSA with SHA256
signature = private_key.signal(
message,
ec.ECDSA(hashes.SHA256())
)
# Convert the DER-encoded signature to uncooked format (r and s concatenated)
der_to_raw_ecdsa_format = lambda der: der[4:36] + der[-32:]
# Convert the signature to uncooked format (64 bytes)
signature_64 = der_to_raw_ecdsa_format(signature)
# Base64 URL-encode the signature
signature_base64 = base64.urlsafe_b64encode(signature_64).decode().rstrip("=")
# Concatenate header, payload, and signature to kind the JWT
jwt_token = f"{header_base64}.{payload_base64}.{signature_base64}"
return jwt_token
def get_apple_public_keys():
attempt:
# Generate a contemporary JWT
developer_token = generate_apple_developer_token()
# Arrange headers with the authorization token
headers = {
"Authorization": f"Bearer {developer_token}"
}
# Fetch the general public keys from Apple
response = requests.get('https://api.storekit.itunes.apple.com/in-app-purchase/publicKeys', headers=headers)
# Log the response if it isn't profitable
if response.status_code != 200:
print(f"Error fetching public keys: {response.status_code} {response.textual content}")
response.raise_for_status() # Raises an exception for 4xx/5xx errors
# Parse and return the general public keys
response_data = response.json()
keys = response_data.get('keys')
if not keys:
print("No 'keys' discovered within the response from Apple.")
return []
return keys
besides requests.exceptions.RequestException as e:
print(f"Error fetching Apple's public keys: {e}")
return []
I additionally tried utilizing jwt to implement the jwt token
def generate_apple_developer_token():
with open(PRIVATE_KEY_FILE, 'r') as f:
private_key = f.learn().strip()
print('that is the important thing', private_key)
# JWT header
headers = {
"alg": "ES256", # Apple makes use of ES256 (Elliptic Curve)
"child": KEY_ID,
"typ": "JWT"
}
# JWT payload
payload = {
"iss": ISSUER_ID,
"iat": int(datetime.utcnow().timestamp()), # Issued at time
"exp": int((datetime.utcnow() + timedelta(minutes=10)).timestamp()), # Expiration (max 10 minutes)
"aud": "appstoreconnect-v1",
}
# Generate and return the JWT
return jwt.encode(payload, private_key, algorithm="ES256", headers=headers)
In both case, I checked the token in jwt.io and they’re right, however for some cause they fail the authentication