{"record":{"id":"5a07daabaf63d7c5","repo":"RocketChat/Rocket.Chat","slug":"failed-to-refresh-apple-public-keys-using-stale-c","errorCode":null,"errorMessage":"Failed to refresh Apple public keys, using stale cache","messagePattern":"Failed to refresh Apple public keys, using stale cache","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts","lineNumber":56,"sourceCode":"\ttry {\n\t\tconst response = await fetch('https://appleid.apple.com/auth/keys', {\n\t\t\tmethod: 'GET',\n\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;","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts#L38-L74","documentation":"To verify Sign in with Apple identity tokens, the server fetches Apple's JWKS from https://appleid.apple.com/auth/keys; the fetch failed (network error or non-OK HTTP status). If a previously fetched key set is cached, it is returned as a stale cache and logins continue with the old keys; with no cache the function throws 'Could not retrieve Apple public keys' and Apple sign-in fails entirely.","triggerScenarios":"Outbound HTTPS to appleid.apple.com blocked by firewall/proxy; Apple endpoints returning non-200; TLS interception with a CA Node does not trust; DNS failures. Apple logins keep working on stale keys until Apple rotates its signing key.","commonSituations":"Locked-down servers without an egress allowlist entry for appleid.apple.com; corporate proxies requiring custom CAs (missing NODE_EXTRA_CA_CERTS); transient Apple outages observed as repeated warns with successful fallback.","solutions":["Allow outbound HTTPS to appleid.apple.com from the Rocket.Chat server","Behind TLS-inspecting proxies, set HTTPS_PROXY and NODE_EXTRA_CA_CERTS so the fetch succeeds","If logins fail with 'Could not retrieve Apple public keys', the cache was empty — fix egress before anything else","No urgent action while logins still work: stale keys are valid until Apple rotates; monitor for the throwing variant"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// health check: verify egress to Apple's JWKS endpoint before enabling Sign in with Apple\nconst res = await fetch('https://appleid.apple.com/auth/keys');\nif (!res.ok) throw new Error(`Apple JWKS unreachable: ${res.status}`);","typeGuard":null,"tryCatchPattern":"try {\n\treturn await fetchAppleKeys();\n} catch (error) {\n\tif (cachedKeys) {\n\t\tconsole.warn('Failed to refresh Apple public keys, using stale cache', error);\n\t\treturn cachedKeys; // valid until Apple rotates its signing key\n\t}\n\tthrow new Error('Could not retrieve Apple public keys', { cause: error });\n}","preventionTips":["Allowlist outbound HTTPS to appleid.apple.com in firewall/proxy rules","Set NODE_EXTRA_CA_CERTS/HTTPS_PROXY when behind TLS inspection","Alert on repeated warns: stale-cache mode ends the moment Apple rotates keys, breaking all Apple logins"],"tags":["apple","oauth","jwks","network","auth","egress"],"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"}