WeatherKit REST API Authentication

Hi there! Please could you publish some of the specifics for authentication for the new WeatherKit REST API? I am attempting to use something cobbled together from MapKit, and unfortunately WeatherKit returns a 500 error in response.

Broken code sample below. If you find a working solution or can identify an error, please comment with the solution. Thank you!

require 'http'
require 'jwt'
require 'openssl'
require 'logger'

p8 = OpenSSL::PKey::EC.new(File.read("/PATH/TO/DOWNLOADED/P8/FILE"))

token = JWT.encode({
  iss: 'KEY_NAME_FROM_CIP',
  iat: Time.now.to_i,
  exp: Time.now.to_i + 3600,
  aud: 'weatherkit',
}, p8, 'ES256', {
  kid: 'KEY_ID_FROM_CIP',
  typ: 'JWT'
})

res = HTTP.use(logging: {
  logger: Logger.new(STDOUT)
}).headers(
  "Authorization" => "Bearer #{token}"
).get "https://weatherkit.apple.com/api/v1/weather/en/LONG/LAT?dataSets=currentWeather&timezone=Europe/London"

puts "code: #{res.code}"
puts res.body

I'm the same here. Apparently, the APIs are still not available.

I was really hoping the session would be more informative, but they simply said "[you need to] create the header containing the fields and values described in the developer documentation", when the developer documentation literally has no mention that this API uses JWTs.

@leemartin reached out to Novall on Twitter to ask when the REST API will be functional, but hasn't received a response yet. Twitter isn't allowed to be linked, but the text of the link is permitted: https://twitter.com/leemartin/status/1534214645128564737

Really excited to get cracking with this, but looks like it's half-baked at the moment.

This video contains some details about WeatherKit Auth starting at 9:05 - https://developer.apple.com/videos/play/wwdc2022/10003/

The example code in the video omitted the "Bearer " prefix from the Authorization header

Seems like there was an update this night. Now I'm getting 401 instead of 500.

Anyone get this working yet?

I'm getting a 500 with a node app

Looks like there is a url issue? If I use the urls in the docs:

https://weatherkit.apple.com/api/v1/weather/en

then I get a 500.

If I use the url as in the video

https://weatherkit.apple.com/1/weather/

then I get a 401

I've opened a feedback request (10140066) with the following:

WeatherKit REST API documentation is missing authentication details

The API documentation for WeatherKit's REST API is missing the authentication details that were mentioned during the session video. Several other engineers have also reported this via the forums, without any official response. To reproduce, visit https://developer.apple.com/documentation/weatherkitrestapi/get_api_v1_weather_language_latitude_longitude and observe the lack of details regarding authentication. I expect to be able to understand how to authenticate with this API from its documentation, but the documentation is missing, however, any reference to authentication is missing. I am not using a version of Xcode to encounter this issue.

Would really appreciate a timeline from Apple for a resolution!

Additionally, @markdaws on Twitter has also followed up to another WeatherKit tweet expressing issue with authentication for this API. Tweet links as code are now banned, so here's a gigantic picture instead:

SUCCESS! Had a labs call with two Apple engineers and they set me on the right path.

This is what your JWT should look like:

var privateKey = fs.readFileSync("YOU KEY FILE FROM DEVELOPER CENTER.p8");
  var token = jwt.sign(
    {
      subject: "APP ID",
    },
    privateKey,
    {
      jwtid: "YOUR TEAM ID.YOUR APP ID",
      issuer: "TEAM ID",
      expiresIn: "1h",
      keyid: "KEY ID",
      algorithm: "ES256",
      header: {
        id: "YOUR TEAM ID.YOUR APP ID",
      },
    }
  );
  // id is teamid.serviceid

  const url =
    "https://weatherkit.apple.com/api/v1/weather/en/51.677612/-2.937941?dataSets=currentWeather&timezone=Europe/London";

  const config = {
    headers: { Authorization: `Bearer ${token}` },
  };

  const { data: weatherData } = await axios.get(url, config);

Getting the id on the header is the trick.

I have made a YouTube video and example app but it won't let me post the link, the youtube video id is: 7mg42_Fix9k

@tanakasan1734 you legend, thank you for staying up till 2am working on this!!

Thank you so much for putting this up as a YouTube video as well, really appreciate it! I've converted your JavaScript example into my native Ruby & included it below.

For those still having issues with this API: of significant importance is setting up an "app" in Certificates, Identifiers & Profiles in addition to setting up the "key", so you have an "app ID" registered for WeatherKit in addition to the key being assigned access to it. I would imagine this has something to do with the upcoming billing for WeatherKit.

Full annotated example from my lab notebook:

# WeatherKit REST API: access example
# With thanks to Simon of All The Code (Twitter: @allthecode_)

require 'openssl' # stdlib
require 'logger' # stdlib
require 'http' # gem install http
require 'jwt' # gem install jwt

## APPLE ID's (mine are examples)
TEAM_ID = "TVVX2ENCS3" # Your Apple Developer "team ID", see https://developer.apple.com/account/#!/membership -> "Team ID"
APP_ID = "net.rubynerd.weathervane" # Create an Identifier -> App ID -> App -> set Bundle ID -> App Services -> check WeatherKit -> Save, use Bundle ID here
KEY_ID = "HC2L4UY9UW" # Create a Key -> set Key Name -> check WeatherKit, use Key ID here
KEY_PATH = "/Users/rubynerd/Downloads/AuthKey_HC2L4UY9UW.p8" # Path to file downloaded from key creation above

## GEO VARS
# Charing Cross, nominal centre of London
LAT = "51.5081"
LONG = "0.1248"
TZ = "Europe/London"
DATASETS = %w{currentWeather}.join(",")

# Read the key path to create an ECDSA key
p8 = OpenSSL::PKey::EC.new(File.read(KEY_PATH))

# Create a JWT for accessing WeatherKit
jwt = JWT.encode({
  iss: TEAM_ID,
  iat: Time.now.to_i,
  exp: Time.now.to_i + 3600,
  sub: APP_ID,
  jti: "#{TEAM_ID}.#{APP_ID}",
}, p8, 'ES256', {
  kid: KEY_ID,
  id: "#{TEAM_ID}.#{APP_ID}",
})

# Use httprb (https://github.com/httprb/http) to make the request to WeatherKit
res = HTTP.use(logging: {
  logger: Logger.new(STDOUT)
}).headers(
  "Authorization" => "Bearer #{jwt}",
).get "https://weatherkit.apple.com/api/v1/weather/en/#{LAT}/#{LONG}?dataSets=#{DATASETS}&timezone=#{TZ}"

# Parse the response body from JSON to a Ruby Hash
body = res.parse

# Print the condition code
puts "conditionCode: #{body["currentWeather"]["conditionCode"]}"
# Print required attribution & link
puts "data provided by Apple WeatherKit (#{body["currentWeather"]["metadata"]["attributionURL"]})"

If you're translating the above from Ruby to another language, I've included an expired JWT you can paste into jwt.io's debugger:

eyJraWQiOiJIQzJMNFVZOVVXIiwiaWQiOiJUVlZYMkVOQ1MzLm5ldC5ydWJ5bmVyZC53ZWF0aGVydmFuZSIsImFsZyI6IkVTMjU2In0.eyJpc3MiOiJUVlZYMkVOQ1MzIiwiaWF0IjoxNjU0ODU0NTU5LCJleHAiOjE2NTQ4NTQ1NjQsInN1YiI6Im5ldC5ydWJ5bmVyZC53ZWF0aGVydmFuZSIsImp0aSI6IlRWVlgyRU5DUzMubmV0LnJ1YnluZXJkLndlYXRoZXJ2YW5lIn0.7mGJZvce6D2JmbligmurJc0H4sMa_CwCwHBB4a5yoQvh9n7AljVOpDp7vHblWRG-DPtSqSFzOflM92otKkgQSw

Between the JS and Ruby, this should be enough to close this. I'll leave the feedback request open to push for accurate documentation. Let me know if you have any issues with what's above, or DM me on Twitter if you need further assistance.

Apple have published docs on this topic now and also modified the API so that now your JWT subject has to be sub and not subject as I was able to get away with in my first code example.

I ended up integrating this on a new project already. 😅 https://www.3000degreez.app (Dev blog coming soon)

This setup had been working well for me until yesterday, and now I'm unable to authenticate. Has anyone else experienced this issue as well?

Same here... it worked in the betas, but stopped working now that I want to publish :( Weatherkit is enabled in both Capacities, and in the App Services...

I have decoded the JWT token and ensured it contains the exact same items as the sample token at https://developer.apple.com/documentation/weatherkitrestapi/request_authentication_for_weatherkit_rest_api

{
    "alg": "ES256",
    "kid": "3J4F34**24",
    "id": "KC697SNQ2X.com.caramba.instaweather"
}
{
    "iss": "KC697SNQ2X",
    "iat": 1437179036,
    "exp": 1493298100,
    "sub": "com.caramba.instaweather"
}

response is

{"timestamp":"2022-09-16T18:17:15Z","status":403,"error":"Forbidden","message":"Access Denied","path":"/api/v1/availability/37.323/122.032"}   
WeatherKit REST API Authentication
 
 
Q