RocketChat/Rocket.Chat · warning

Failed to refresh Apple public keys, using stale cache

Error message

Failed to refresh Apple public keys, using stale cache

What it means

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.

Source

Thrown at apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts:56

	try {
		const response = await fetch('https://appleid.apple.com/auth/keys', {
			method: 'GET',
			// SECURITY: Hardcoded URL, no SSRF protection needed
			ignoreSsrfValidation: true,
		});

		if (!response.ok) {
			throw new Error(`Failed to fetch Apple keys: ${response.status} ${response.statusText}`);
		}

		const data = (await response.json()) as { keys: AppleJWK[] };
		cachedKeys = data.keys;
		lastFetchTime = now;

		return cachedKeys;
	} catch (error) {
		if (cachedKeys) {
			console.warn('Failed to refresh Apple public keys, using stale cache', error);
			return cachedKeys;
		}
		throw new Error('Could not retrieve Apple public keys', { cause: error });
	}
}

function decodeBase64Url(str: string): string {
	return Buffer.from(str, 'base64url').toString('utf8');
}

async function verifyAppleJWT(
	headerB64: string,
	payloadB64: string,
	signatureB64: string,
	clientId: string,
): Promise<AppleJWTPayload | null> {
	const header = JSON.parse(decodeBase64Url(headerB64));
	const payload = JSON.parse(decodeBase64Url(payloadB64)) as AppleJWTPayload;

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Allow outbound HTTPS to appleid.apple.com from the Rocket.Chat server
  2. Behind TLS-inspecting proxies, set HTTPS_PROXY and NODE_EXTRA_CA_CERTS so the fetch succeeds
  3. If logins fail with 'Could not retrieve Apple public keys', the cache was empty — fix egress before anything else
  4. No urgent action while logins still work: stale keys are valid until Apple rotates; monitor for the throwing variant
Defensive patterns

Strategy: fallback

Validate before calling

// health check: verify egress to Apple's JWKS endpoint before enabling Sign in with Apple
const res = await fetch('https://appleid.apple.com/auth/keys');
if (!res.ok) throw new Error(`Apple JWKS unreachable: ${res.status}`);

Try / catch

try {
	return await fetchAppleKeys();
} catch (error) {
	if (cachedKeys) {
		console.warn('Failed to refresh Apple public keys, using stale cache', error);
		return cachedKeys; // valid until Apple rotates its signing key
	}
	throw new Error('Could not retrieve Apple public keys', { cause: error });
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/5a07daabaf63d7c5. Report an issue: GitHub.