{"record":{"id":"d1cea9f8bffb8bf6","repo":"RocketChat/Rocket.Chat","slug":"could-not-retrieve-apple-public-keys","errorCode":null,"errorMessage":"Could not retrieve Apple public keys","messagePattern":"Could not retrieve Apple public keys","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts","lineNumber":59,"sourceCode":"\t\t\t// SECURITY: Hardcoded URL, no SSRF protection needed\n\t\t\tignoreSsrfValidation: true,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to fetch Apple keys: ${response.status} ${response.statusText}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as { keys: AppleJWK[] };\n\t\tcachedKeys = data.keys;\n\t\tlastFetchTime = now;\n\n\t\treturn cachedKeys;\n\t} catch (error) {\n\t\tif (cachedKeys) {\n\t\t\tconsole.warn('Failed to refresh Apple public keys, using stale cache', error);\n\t\t\treturn cachedKeys;\n\t\t}\n\t\tthrow new Error('Could not retrieve Apple public keys', { cause: error });\n\t}\n}\n\nfunction decodeBase64Url(str: string): string {\n\treturn Buffer.from(str, 'base64url').toString('utf8');\n}\n\nasync function verifyAppleJWT(\n\theaderB64: string,\n\tpayloadB64: string,\n\tsignatureB64: string,\n\tclientId: string,\n): Promise<AppleJWTPayload | null> {\n\tconst header = JSON.parse(decodeBase64Url(headerB64));\n\tconst payload = JSON.parse(decodeBase64Url(payloadB64)) as AppleJWTPayload;\n\n\tconst nowInSeconds = Math.floor(Date.now() / 1000);\n","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts#L41-L77","documentation":"Thrown by getApplePublicKeys when fetching https://appleid.apple.com/auth/keys fails and there is no previously cached key set to fall back on. Apple's JWKS are required to verify Sign-in-with-Apple identityToken signatures. Keys are cached in-process for 24h and a stale cache is served if a refresh fails, so this error means the very first fetch (cold start) failed: outbound network to Apple is not working.","triggerScenarios":"Apple OAuth login on a server with blocked internet egress (firewall/proxy/DNS) so the JWKS fetch throws or returns non-200; Apple endpoint temporarily unavailable during a cold start; proxy misconfiguration stripping the request; TLS interception breaking the connection.","commonSituations":"Self-hosted instances in air-gapped or egress-restricted networks; containers without proxy env vars; on-prem installs where only specific domains are allow-listed and appleid.apple.com is missing.","solutions":["Allow outbound HTTPS to appleid.apple.com from the Rocket.Chat server process (proxy/allow-list/firewall)","Configure the HTTP(S)_PROXY environment for the server so server-fetch reaches Apple","Retry after connectivity is fixed; once one fetch succeeds, the 24h cache absorbs short Apple outages"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-flight egress check at startup\ntry {\n\tconst res = await fetch('https://appleid.apple.com/auth/keys');\n\tif (!res.ok) throw new Error(`Apple JWKS unreachable: ${res.status}`);\n} catch (e) {\n\tlogger.error('Apple login will fail: no egress to appleid.apple.com', e);\n}","typeGuard":null,"tryCatchPattern":"try {\n\tconst serviceData = await handleIdentityToken(identityToken, clientId);\n} catch (e) {\n\tif (e instanceof Error && e.message === 'Could not retrieve Apple public keys') {\n\t\t// transient network / egress issue: inform user, retry with backoff; check e.cause\n\t} else throw e;\n}","preventionTips":["Allow-list outbound HTTPS to appleid.apple.com in firewalls/proxies for all Rocket.Chat nodes","Monitor egress with a startup/periodic JWKS fetch so failures surface before users hit login","Keep processes warm: once one fetch succeeds the 24h cache bridges short Apple outages"],"tags":["apple-oauth","network","jwks","egress","sign-in-with-apple"],"backgroundTag":"jwks-fetch-failed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}