Skip to content

Fix where apple is setting the service key now - #6

Merged
MattKiazyk merged 2 commits into
mainfrom
fix-asc-key-not-olympus
Sep 12, 2026
Merged

MattKiazyk merged 2 commits into
mainfrom
fix-asc-key-not-olympus

Conversation

@MattKiazyk

@MattKiazyk MattKiazyk commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Updates XcodesLoginKit’s Apple service-key resolution to follow the approach introduced in fastlane/fastlane#30206.

Apple’s legacy App Store Connect Olympus configuration endpoint now returns 404, making the previous widget-key lookup unreliable. Apple currently exposes the active widgetKey through the unauthenticated App Store Connect /logout redirect.

This PR removes the hardcoded service key and introduces a cached, multi-source resolver that can discover Apple’s current key without requiring an XcodesLoginKit release whenever Apple rotates it.

Service-key resolution order

On the first resolution attempt, XcodesLoginKit now tries:

  1. An explicitly supplied AppleServiceKeyProvider, when configured.
  2. The best-effort on-disk cache.
  3. The widgetKey from App Store Connect’s unauthenticated /logout redirect.
  4. The legacy Olympus configuration endpoint as a final fallback.

Once resolved, the key is retained in the resolver’s in-memory cache for subsequent authentication requests.

Successful automatic lookups are also written to disk. Cache read and write failures are logged but do not prevent authentication.

App Store Connect redirect lookup

The new primary automatic lookup performs:

HEAD https://appstoreconnect.apple.com/logout

Apple responds with a redirect similar to:

https://idmsa.apple.com/appleauth/signout?widgetKey=<current-key>&asop=destroy-session&...

XcodesLoginKit extracts widgetKey from the Location header without following the redirect.

Sign-out safety

The redirect ultimately performs a real sign-out, so the lookup uses a dedicated ephemeral URLSession configured to:

  • Send no authentication cookies.
  • Store no response cookies.
  • Use no shared cookie storage.
  • Ignore cached responses.
  • Refuse redirect following.
  • Avoid using the client’s authenticated Apple session.

This prevents service-key discovery from accidentally invalidating an existing Apple session.

Hardcoded key removal

The bundled hardcoded App Store Connect widget key has been removed.

A stale hardcoded key can appear to work through SRP and two-factor authentication, only for Apple to reject the resulting session later with an unrelated 401. Resolving Apple’s current key up front avoids that misleading failure and supports future key rotations without requiring a library update.

Applications can still override discovery explicitly:

let client = Client(
    serviceKeyProvider: .fixed("current-public-widget-key")
)

Dynamic configuration providers remain supported:

let client = Client(
    serviceKeyProvider: AppleServiceKeyProvider {
        try await configuration.appleServiceKey()
    }
)

Typed failure reporting

Service-key failures now preserve information about every attempted source:

AuthenticationError.serviceKeyResolutionFailed(
    attempts: [AppleServiceKeyAttempt]
)

Each AppleServiceKeyAttempt contains:

  • The source that was attempted.
  • The specific reason it failed.
  • The HTTP status and a bounded response-body preview when available.
  • Whether the failure is potentially retryable.

Possible failure reasons include:

  • Network failure.
  • Non-HTTP response.
  • Unexpected HTTP status.
  • Missing redirect.
  • Invalid redirect URL.
  • Missing or empty service key.

Example localized error:

Could not retrieve Apple's sign-in service key. App Store Connect sign-out redirect: did not return a Location header; App Store Connect Olympus endpoint: returned HTTP 404 (Not Found)

Authentication failures that happen after a key has been resolved are no longer caught and incorrectly reported as service-key resolution failures.

Error-handling behavior

Expected transport and parsing failures from the sign-out lookup fall through to Olympus.

Unexpected local or programming errors are preserved and rethrown instead of being swallowed as fallback failures. Task cancellation is also propagated immediately and does not trigger another network request.

This follows Fastlane’s approach of avoiding broad error handling around the lookup path.

Concurrency

The resolver is implemented as an actor so its in-memory key state is concurrency-safe.

Provider and network loaders are @Sendable, and cancellation is checked between asynchronous operations. The no-redirect session delegate is stateless, with its unchecked Sendable conformance documented.

Documentation

The README now documents:

  • The new resolution order.
  • Explicit provider configuration.
  • In-memory and on-disk caching.
  • Why the logout lookup requires a separate cookie-free session.
  • The new detailed failure type.

Tests

Added and updated coverage for:

  • Explicit service-key providers.
  • Extracting widgetKey from the App Store Connect redirect.
  • Using HEAD for the logout request.
  • Preventing cookie handling and redirect following.
  • Falling back when the redirect has no Location header.
  • Falling back when the logout request fails.
  • Falling back to Olympus when the redirect is malformed.
  • Reporting Olympus HTTP status and response-body details.
  • Retryable versus non-retryable failures.
  • In-memory key reuse.
  • On-disk cache reads and writes.
  • Best-effort cache failures.
  • Propagating unexpected errors.
  • Propagating task cancellation without trying Olympus.
  • Ensuring authentication failures are not converted into service-key failures.
  • Loading an explicit provider only once.
  • An opt-in live test against Apple’s actual logout endpoint.

Verification

  • swift test
    • 33 tests passed.
    • 1 opt-in live test skipped during the normal suite.
  • Live App Store Connect redirect test:
    • XCODES_LOGIN_KIT_LIVE_SERVICE_KEY_TEST=1 swift test --filter testLiveAppStoreConnectSignOutRedirectContainsServiceKey
  • swift build -c release
  • LoginKitExample macOS Debug build
  • git diff --check

The live test confirmed that the exact Swift no-cookie/no-redirect code path currently retrieves a valid 32–64 character lowercase hexadecimal widget key from Apple. No Apple ID credentials are required for this lookup.

@MattKiazyk
MattKiazyk merged commit 929f9aa into main Sep 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant