WeatherKit REST API 401 error: { reason: 'NOT_ENABLED' }

Hey there,

I'm attempting to build a simple Node/Express server via TypeScript, and I can't get a simple request to the REST API to work. I've checked my JWToken (after decoding it) and it matches what Apple is requests, and what other have shown to work in their node servers.

I created a separate key to use for this node app, in case it was some conflict with my key for my iOS app.

Any guesses as to why?

Here's the code in question with private info removed:

var jwtToken = jwt.sign({ subject: 'my.company' }, 
    privateKey, 
    { 
    jwtid: '#########.my.company',
    issuer: 'myTeamID',
    expiresIn: '1hr',
    keyid: '#########',
    algorithm: 'ES256',
    header: { alg: 'ES256', id: 'myTeamID.my.company' } as JwtHeader,
});

export async function requestWeather() {
    console.log('Token:', jwtToken)
    const authHeader = {
        headers: { Authorization: `Bearer ${jwtToken}`}
    }
    const url = "https://weatherkit.apple.com/api/v1/weather/en/37.323/122.032?dataSets=currentWeather";
    const request = await axios.get(url, authHeader)
    console.log(request)
}
Answered by VoyagerSpace in 742816022

Solving my own thread:

The issue here is in the very first line of my code:

var jwtToken = jwt.sign({ subject: 'my.company' },

"subject" should be "sub"

var jwtToken = jwt.sign({ sub: 'my.company' },

That was literally it. Apple must have changed the expected format of the JWT, because all the videos about the API after it was released show developers using "subject".

Hope this helps someone in the future.

This is the response from the server that I forgot to add yesterday

response: {    status: 401,    statusText: 'Unauthorized',    headers: AxiosHeaders {      server: 'AppleHttpServer/0716b0c1a3a0f9c2fa220619c9901ea42a9aac04',      'x-frame-options': 'DENY',      'strict-transport-security': 'max-age=31536000; includeSubDomains, max-age=31536000',      'x-xss-protection': '1; mode=block',      'access-control-allow-origin': '*',      'x-content-type-options': 'nosniff',      'content-security-policy': "default-src 'self';",      'x-request-id': '6d6f842a-cb5e-4e28-abd8-e4486faa87ec',      'x-b3-traceid': '4d098ad911e603a4',      date: 'Sat, 21 Jan 2023 20:32:32 GMT',      'x-cache': 'TCP_MISS from a23-220-165-69.deploy.akamaitechnologies.com (AkamaiGHost/10.10.3-45298580) (-)',      connection: 'close'    },    config: {      transitional: [Object],      adapter: [Array],      transformRequest: [Array],      transformResponse: [Array],      timeout: 0,      xsrfCookieName: 'XSRF-TOKEN',      xsrfHeaderName: 'X-XSRF-TOKEN',      maxContentLength: -1,      maxBodyLength: -1,      env: [Object],      validateStatus: [Function: validateStatus],      headers: [AxiosHeaders],      method: 'get',      url: 'https://weatherkit.apple.com/api/v1/weather/en/37.323/122.032?dataSets=currentWeather',      data: undefined    },    request: <ref *1> ClientRequest {      _events: [Object: null prototype],      _eventsCount: 7,      _maxListeners: undefined,      outputData: [],      outputSize: 0,      writable: true,      destroyed: false,      _last: true,      chunkedEncoding: false,      shouldKeepAlive: false,      maxRequestsOnConnectionReached: false,      _defaultKeepAlive: true,      useChunkedEncodingByDefault: false,      sendDate: false,      _removedConnection: false,      _removedContLen: false,      _removedTE: false,      strictContentLength: false,      _contentLength: 0,      _hasBody: true,      _trailer: '',      finished: true,      _headerSent: true,      _closed: false,      socket: [TLSSocket],      _header: 'GET /api/v1/weather/en/37.323/122.032?dataSets=currentWeather HTTP/1.1\r\n' +        'Accept: application/json, text/plain, */*\r\n' +        'Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjlETDRYVVNOVTYiLCJpZCI6IjU5WDlYVVo5OTQudm95YWdlci5mYWlyV2VhdGhlckZyaWVuZHMifQ.eyJzdWJqZWN0Ijoidm95YWdlci5mYWlyV2VhdGhlckZyaWVuZHMiLCJpYXQiOjE2NzQzMzMxMTgsImV4cCI6MTY3NDMzNjcxOCwiaXNzIjoiNTlYOVhVWjk5NCIsImp0aSI6IjU5WDlYVVo5OTQudm95YWdlci5mYWlyV2VhdGhlckZyaWVuZHMifQ.Nft6qHN8rWecBwKyvBBa6MmehrqA0UotW44bKm8UGld0GVKN9Jnn6YYPOesYRHcpXMb7k6auExbvqynRS2TFeQ\r\n' +        'User-Agent: axios/1.2.3\r\n' +        'Accept-Encoding: gzip, compress, deflate, br\r\n' +        'Host: weatherkit.apple.com\r\n' +        'Connection: close\r\n' +        '\r\n',      _keepAliveTimeout: 0,      _onPendingData: [Function: nop],      agent: [Agent],      socketPath: undefined,      method: 'GET',      maxHeaderSize: undefined,      insecureHTTPParser: undefined,      path: '/api/v1/weather/en/37.323/122.032?dataSets=currentWeather',      _ended: true,      res: [IncomingMessage],      aborted: false,      timeoutCb: null,      upgradeOrConnect: false,      parser: null,      maxHeadersCount: null,      reusedSocket: false,      host: 'weatherkit.apple.com',      protocol: 'https:',      _redirectable: [Writable],      [Symbol(kCapture)]: false,      [Symbol(kBytesWritten)]: 0,      [Symbol(kEndCalled)]: true,      [Symbol(kNeedDrain)]: false,      [Symbol(corked)]: 0,      [Symbol(kOutHeaders)]: [Object: null prototype],      [Symbol(kUniqueHeaders)]: null    },    data: { reason: 'NOT_ENABLED' }  }
Accepted Answer

Solving my own thread:

The issue here is in the very first line of my code:

var jwtToken = jwt.sign({ subject: 'my.company' },

"subject" should be "sub"

var jwtToken = jwt.sign({ sub: 'my.company' },

That was literally it. Apple must have changed the expected format of the JWT, because all the videos about the API after it was released show developers using "subject".

Hope this helps someone in the future.

WeatherKit REST API 401 error: { reason: 'NOT_ENABLED' }
 
 
Q