Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

ASC API key auth fails with Apple 401 because @expo/apple-utils signs JWTs with exactly the 1200 s maximum lifetime

Open Beginner friendly
#4,623 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 3 days

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
68/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
ios, node.js, typescript

Research direction

The fault is in Token.sign in @expo/apple-utils/build/index.js, where o=1200 and Math.min(o, duration ?? o) set exp = iat + 1200. Start by reproducing with apple.BundleId.getAsync and new Token({ duration: 1200 }) from eas-cli/node_modules/@expo/apple-utils, using the issue's probe table. Done when a token signed at the default duration returns 200 from GET /v1/bundleIds?limit=1.

Written by the indexing model from the issue text.

Description

Build/Submit details page URL

https://expo.dev/accounts/isaaceast/projects/MyApp/builds/42ae2260-bdef-4ba5-86af-e698dc3e9659 (the build itself succeeded after falling back to Apple ID login; the failure is in the credentials step before it)

Summary

With a valid App Store Connect team API key provided via EXPO_ASC_API_KEY_PATH, EXPO_ASC_KEY_ID, EXPO_ASC_ISSUER_ID (+ EXPO_APPLE_TEAM_ID, EXPO_APPLE_TEAM_TYPE), eas build -p ios fails during "Linking bundle identifier" with:

Auth error: 'm', 'Apple 401 detected - You are either not logged in, your account doesn't have access to the requested data, or the page doesn't exist
Authentication credentials are missing or invalid. - Provide a properly configured and signed bearer token, and make sure that it has not expired.'. Login in again (remaining: 3)
Log in to your Apple Developer account to continue

and then falls back to the Apple ID login flow (which hangs in --non-interactive).

The key is valid: a JWT signed by hand from the same .p8/Key ID/Issuer ID returns 200 from GET /v1/bundleIds.

Root cause

@expo/apple-utils Token.sign uses o=1200 and Math.min(o, duration ?? o), so every token has exp = iat + 1200, which is Apple's hard maximum lifetime. Apple appears to evaluate exp - serverNow <= 1200 with second resolution. If the client clock is even ~1 s ahead of Apple's (my machine: local 05:44:36 vs Apple Date: header 05:44:35), the token is 1201 s from Apple's point of view and is rejected.

Probing with hand-signed tokens against GET /v1/bundleIds?limit=1:

lifetime result
600 s 200
1140 s 200
1199 s 200
1200 s 401
1200 s with iat set 5 s in the past 200
1201 s 401

Calling apple.BundleId.getAsync({ token: new Token({ key, issuerId, keyId, duration: 1200 }) }, ...) directly from eas-cli/node_modules/@expo/apple-utils reproduces the 401; patching the constant to 1140 makes the same call succeed 3/3 and lets eas submit:status -p ios --non-interactive read App Store Connect.

Suggested fix

Sign with a safety margin below Apple's limit (for example 1140 s or min(duration, 1200) - 60), or set iat slightly in the past. Apple's own documentation only guarantees tokens valid for at most 20 minutes, so signing exactly at the limit is fragile against any positive clock skew.

Environment
  • eas-cli 24.13.0, Node 24.20.0, Windows 11 (clock within ~1 s of time.windows.com)
  • Expo SDK 54, Individual Apple Developer team
  • Reproduces with two different keys (App Manager and Admin roles)
Workaround

Patch @expo/apple-utils/build/index.js in the installed eas-cli: o=1200 -> o=1140.

Dominant language
TypeScript
Stars
1.4k
Forks
254
Avg merge
3d 22h
Merged PRs (30d)
109

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from expo/eas-cli

All issues in expo/eas-cli

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.