DeviceCheck调用异常:Apple服务器返回401无法验证授权令牌
I'm trying to implement Apple's DeviceCheck feature, but keep getting a 401 Unable to verify authorization token response from Apple's servers. The device_token is sent as a Base64-encoded string in the JSON payload to my Python server. Here's my code example:
def device_check_query(device_token): data = { 'device_token': device_token, 'transaction_id': str(uuid4()), 'timestamp': int(time.time() * 1000), } jw_token = get_jw_token() headers = {'Authorization': 'Bearer ' + jw_token} response = requests.post(QUERY_URL, json=data, headers=headers) return response.content def get_jw_token(): with open(KEY_FILE, 'r') as cert_file: certificate = cert_file.read() jw_token = jwt.encode( {'iss': TEAM_ID}, certificate, algorithm='ES256', headers={'kid': KEY_ID}) return jw_token
Let's walk through the most likely causes for this 401 error and how to fix them:
1. Incorrect Private Key Handling
Your current code reads the .p8 key as a raw text string, but ES256 requires the private key to be parsed properly. Apple's DeviceCheck private keys are in PEM format, and loading them as plain text can lead to invalid signatures. Try using the cryptography library to load the key correctly:
from cryptography.hazmat.primitives import serialization def get_jw_token(): with open(KEY_FILE, 'rb') as cert_file: private_key = serialization.load_pem_private_key( cert_file.read(), password=None, ) jw_token = jwt.encode( {'iss': TEAM_ID}, private_key, algorithm='ES256', headers={'kid': KEY_ID}) return jw_token
Also double-check that your key file includes the full -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- headers with no extra whitespace or line breaks.
2. Mismatched kid or iss Values
These two values are critical for Apple to verify your JWT:
KEY_IDmust be the exact 10-character identifier associated with your DeviceCheck private key (found in the Apple Developer Portal next to the key)TEAM_IDis your 10-character Apple Developer Team ID, not your app's bundle ID or any other identifier
Even a single typo or extra space here will trigger a 401 error.
3. JWT Payload/Header Validation
Apple expects a minimal, correctly formatted JWT:
- The payload should only contain the
issclaim set to your Team ID (your current code does this, but avoid adding extra claims accidentally) - The header must include the
kid(key ID) andalg(set toES256) fields
You can debug your generated JWT by decoding it without verifying the signature:
import jwt decoded = jwt.decode(jw_token, options={"verify_signature": False}) print(decoded) # Check payload print(jwt.get_unverified_header(jw_token)) # Check header
Make sure the output matches exactly what Apple requires.
4. Invalid Device Token Format
Even if you're sending a Base64 string, confirm:
- It's the raw device token received from iOS, encoded with standard Base64 (not URL-safe Base64)
- There are no extra characters like newlines, spaces, or hex encoding in the string
- You haven't accidentally truncated or modified the token before sending it
5. Server Clock Sync Issues
Apple's servers strictly validate the JWT's implicit timestamp. If your server's clock is off by more than a few minutes, the token will be considered expired or not yet valid. Ensure your server has NTP enabled to keep time synchronized accurately.
6. Wrong API Endpoint
Make sure QUERY_URL matches your app's environment:
- Production:
https://api.devicecheck.apple.com/v1/query_two_bits - Sandbox:
https://api.development.devicecheck.apple.com/v1/query_two_bits
Using the wrong endpoint for your build will also result in a 401 error.
内容的提问来源于stack exchange,提问作者Lukas Dambrauskas

