gitroomhq/postiz-app · error · Error

Token request failed: ${error}

Error message

Token request failed: ${error}

What it means

Apple's token endpoint (appleid.apple.com/auth/token) returned a non-2xx response while exchanging the authorization code for an id_token. The response body is embedded in the message, and Apple's body typically contains an error field such as invalid_client or invalid_grant.

Source

Thrown at apps/backend/src/services/auth/providers/apple.provider.ts:94

    const { clientId } = getConfig();
    const response = await fetch('https://appleid.apple.com/auth/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'authorization_code',
        code,
        redirect_uri: redirectUri || defaultRedirect(),
        client_id: clientId,
        client_secret: clientSecret(),
      }).toString(),
    });

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Token request failed: ${error}`);
    }

    const { id_token } = await response.json();
    if (!id_token) {
      throw new Error('Invalid provider token');
    }

    return id_token;
  }

  async getUser(providerToken: string) {
    const { clientId } = getConfig();
    const decoded = decode(providerToken, { complete: true });
    const { keys } = await (
      await fetch('https://appleid.apple.com/auth/keys')
    ).json();

    const key = keys.find(

View on GitHub (pinned to 0f1647f749)

Solutions

  1. Read the appended Apple error text: invalid_client -> fix client_secret generation (key id, team id, .p8, audience https://appleid.apple.com); invalid_grant -> code expired or reused, restart the sign-in flow
  2. Verify APPLE_CLIENT_ID (Services ID for web) matches what's registered in the Apple developer console
  3. Confirm redirect_uri in the token request exactly matches the one configured on the Apple Services ID
  4. Generate a fresh client_secret JWT and confirm its exp is within 6 months and iat is not in the future (clock skew)

Example fix

// before
const clientSecret = jwt.sign(payload, oldKey, { header: { kid: oldKid } });

// after
const clientSecret = jwt.sign(
  { iss: process.env.APPLE_TEAM_ID, aud: 'https://appleid.apple.com', sub: clientId },
  process.env.APPLE_PRIVATE_KEY,
  { algorithm: 'ES256', expiresIn: '1h', header: { kid: process.env.APPLE_KEY_ID } }
);
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

try {
  const token = await appleProvider.getToken(code);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Token request failed:')) {
    const body = e.message.slice('Token request failed:'.length);
    if (body.includes('invalid_grant')) return restartFlow(); // code expired/used
    throw new ConfigurationError('Apple client credentials', body);
  }
  throw e;
}

Prevention

When it happens

Trigger: POST to Apple's token endpoint with an invalid/expired authorization code, wrong client_id, malformed or expired client_secret JWT (the .p8 key secret signed with the wrong key id, team id, or audience), or a redirect_uri mismatch.

Common situations: APPLE_TEAM_ID / APPLE_KEY_ID / APPLE_PRIVATE_KEY / APPLE_CLIENT_ID env vars misconfigured; the signed client_secret JWT passed its 6-month expiry or uses a bad .p8; authorization codes reused (they're single-use, ~5 min TTL); redirect_uri not registered in Apple developer console.

Related errors


AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27). Data as JSON: /api/errors/406f70d46986eb02. Report an issue: GitHub.