keep getting 401 Unauthorized error when fetching Apple's public keys.

I keep getting 401 Unauthorized error when fetching Apple's public keys.

In [14]: print(f"Error fetching public keys: {response.status_code} {response.text}") Error fetching public keys: 401 Unauthenticated

I've verified that the Key ID, Issuer ID, and private key file are all correct, with the private key having admin access. The server time is correctly set to UTC. Given this, I can't identify what might be causing the issue. Any insights?

def generate_apple_developer_token():
    # Load the private key in PEM format
    with open(PRIVATE_KEY_FILE, 'rb') as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
            backend=default_backend()
        )

    # JWT header
    headers = {
        "alg": "ES256",  
        "kid": KEY_ID,   
        "typ": "JWT"   
    }

    # JWT payload
    payload = {
        "iss": ISSUER_ID, 
        "iat": int(datetime.utcnow().timestamp()),  
        "exp": int((datetime.utcnow() + timedelta(minutes=10)).timestamp()), 
        "aud": "appstoreconnect-v1", 
        
    }

    # 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()

    # Sign the message using ECDSA with SHA256
    signature = private_key.sign(
        message,
        ec.ECDSA(hashes.SHA256())
    )

    # Convert the DER-encoded signature to raw format (r and s concatenated)
    der_to_raw_ecdsa_format = lambda der: der[4:36] + der[-32:]

    # Convert the signature to raw 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 form the JWT
    jwt_token = f"{header_base64}.{payload_base64}.{signature_base64}"

    return jwt_token

def get_apple_public_keys():
    try:
        # Generate a fresh JWT
        developer_token = generate_apple_developer_token()

        # Set up headers with the authorization token
        headers = {
            "Authorization": f"Bearer {developer_token}"
        }

        # Fetch the public keys from Apple
        response = requests.get('https://api.storekit.itunes.apple.com/in-app-purchase/publicKeys', headers=headers)
         # Log the response if it's not successful
        if response.status_code != 200:
            print(f"Error fetching public keys: {response.status_code} {response.text}")
        
        response.raise_for_status()  # Raises an exception for 4xx/5xx errors

        # Parse and return the public keys
        response_data = response.json()
        keys = response_data.get('keys')
        if not keys:
            print("No 'keys' found in the response from Apple.")
            return []
        return keys
    except requests.exceptions.RequestException as e:
        print(f"Error fetching Apple's public keys: {e}")
        return []
Answered by App Store Commerce Engineer in 809350022

@luciasalar Have you considered using the App Store Server Library in Python (https://github.com/apple/app-store-server-library-python)? It will handle token generation and calling the appropriate endpoints.

Accepted Answer

@luciasalar Have you considered using the App Store Server Library in Python (https://github.com/apple/app-store-server-library-python)? It will handle token generation and calling the appropriate endpoints.

keep getting 401 Unauthorized error when fetching Apple's public keys.
 
 
Q