I don't have an answer but I'm experiencing the same frustration. I'm trying to use python to generate my JWT then generate a curl command to test in the terminal (because it has verbose messaging).
I created a private key. under "Keys" create a key with the + button. this generates a file AuthKey_{KEY_ID}.p8 where {KEY_ID} is the key_id that was given.
I downloaded the p8 file.
I converted the p8 file to .pem using openssl pkcs8 -nocrypt -in AuthKey_{KEY_ID}.p8 -out AuthKey_{KEY_ID}.pem
I've seen different instructions regarding the creation of the identifier. the documentation says create a ServiceID. an 'influencer' I've tried to follow says to create an App ID identifier. I tried both but have questions: 1) if serviceID do I need to enable "Sign in with Apple". I did not. 2) If AppID, a) which appID Prefix do I choose? I chose the one associated with TeamID, although that was not the default. b) do I tick the box for WeatherKit under the Capabilities or App Services sub-tab? it offers both. I chose both.
3) I created a JWT using python. I have understood that the one 'key' is to get the header to include the APP_ID (the reverse URL). here's what I'm trying but it still fails:
## note remember to update the file path of the .pem file to reflect your username and file location
import jwt
import datetime
TEAM_ID = 'NONYABIZN5'
KEY_ID = '4WHTVRUWNT'
APP_ID = 'com.beispiel.WetterAPI'
file_path = f'/Users/myUser/sandbox/wetterapi/AuthKey_{KEY_ID}.pem'
header = {
"alg": "ES256",
"kid": KEY_ID,
"id": f"{TEAM_ID}.{APP_ID}"
}
current_time = datetime.datetime.utcnow()
expiration_time = current_time + datetime.timedelta(minutes=19)
payload = {
"iss": TEAM_ID,
"iat": int(current_time.timestamp()),
"exp": int(expiration_time.timestamp()),
"sub": APP_ID
}
with open(file_path, 'r') as file:
private_key = file.read()
token = jwt.encode(payload, private_key, algorithm='ES256', headers=header)
# Create the curl command
curl_command = f'curl -v -H "Authorization: Bearer {token}" "https://weatherkit.apple.com/api/v1/availability/37.323/122.032?country=US"'
print(curl_command)
that produces a curl command that I can drop into my terminal. the curl returns:
* Connected to weatherkit.apple.com (2600:1408:c400:58::17d5:9e54) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/cert.pem
* CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN: server accepted http/1.1
* Server certificate:
* subject: CN=weather-data.apple.com; O=Apple Inc.; ST=California; C=US
* start date: Jul 25 21:03:10 2023 GMT
* expire date: Oct 23 21:13:10 2023 GMT
* subjectAltName: host "weatherkit.apple.com" matched cert's "weatherkit.apple.com"
* issuer: CN=Apple IST CA 8 - G1; OU=Certification Authority; O=Apple Inc.; C=US
* SSL certificate verify ok.
* using HTTP/1.1
> GET /api/v1/availability/37.323/122.032?country=US HTTP/1.1
> Host: weatherkit.apple.com
> User-Agent: curl/8.1.2
> Accept: */*
> Authorization: Bearer sdfsfns,dfnanflsandfasdanflknasdlknalsdknlnlnasflnldsfknalasdfknlksandflnsdflnasdflnsaldfknsldakfnlskndflsknadflnsadflnsaldfnaoiw,vm,asfdlkjsdlfklnvlnasdjfsdfadnflsakfndklsfdalknklfsdnlklrewoewu8llnsLSKNlkzxcLKlKNlnskdKVLKSNlknZLKcnLKLKAlkaMLnSDVBisdbdkvbkjvsavskajdbzCXk-2-TasdE-skdfjKJHkjKLlkJLKJlkNLnLKHHiUbKBNhIUHIU
>
< HTTP/1.1 401 Unauthorized
< Server: Apple
< X-Frame-Options: SAMEORIGIN
< Strict-Transport-Security: max-age=31536000; includeSubdomains
< X-XSS-Protection: 1; mode=block
< Access-Control-Allow-Origin: *
< X-Content-Type-Options: nosniff
< Content-Security-Policy: default-src 'self';
< X-REQUEST-ID: 8e5e48ac-410a-4398-b7e6-8d4fbd0454a4
< Date: Thu, 17 Aug 2023 01:56:37 GMT
< X-Cache: TCP_MISS from a23-202-158-148.deploy.akamaitechnologies.com (AkamaiGHost/11.2.2-50400341) (-)
< Connection: close
<
* Closing connection 0
{"reason": "NOT_ENABLED"}%
I've tried this with both "Identifiers" by choosing a suffix to my Reverse URL and using that in one and not using that in the other...
I hope that helps more than it confuses.
I'd love if someone with knowledge can point me (us) in the right direction...