Sandboxed – iOS Security for Builders

Episode 8

Sign in with Apple: Threat Model and Common Mistakes

Sign in with Apple 'works'—but if your backend trusts the client user ID, you've built an account takeover endpoint.

Sign in with Apple isn't just a UI button—it's a cryptographic handshake. Learn why trusting the client-side user identifier is a critical vulnerability, and how to properly verify Apple's identity token on your backend.

🔓The Vulnerability: Trusting the User ID String

⚠️ This is the #1 mistake in Sign in with Apple implementations

Here's the pattern that shows up in code reviews constantly: The iOS client completes Sign in with Apple, receives an ASAuthorizationAppleIDCredential, grabs credential.user, and sends it to the backend: "Log this user in."

The problem: If an attacker ever learns that user identifier—from logs, analytics, intercepted traffic, or screenshots—they can replay it against your backend forever. You've turned account takeover into a copy/paste problem.

Your backend must be able to answer one question:

Did Apple vouch for this user, for my app, right now?

The credential.user string alone cannot answer that. It's a stable label—not proof of identity.

🔐The Correct Model: The Client Is Untrusted

Even though AuthenticationServices does real work—Secure Enclave, Face ID / Touch ID, system UI—your backend can't assume anything about what the client claims.

Treat the client like a browser:

  • The app can present data
  • The backend must verify it

In federated login, the client is where the UX happens—the server is where the security happens.

The Fix: Verify the Identity Token (JWT) on the Server

The Sealed Envelope

Inside the ASAuthorizationAppleIDCredential is a property called identityToken. This is a JWT signed by Apple.

The JWT contains the same user identifier, but it also contains a cryptographic signature you can verify against Apple's public keys.

The Correct Flow

  1. iOS receives the credential from Sign in with Apple
  2. The app sends the identity token (and optionally the authorization code) to your backend
  3. Your backend fetches Apple's public keys from appleid.apple.com/auth/keys
  4. Your backend validates the JWT signature
  5. Your backend verifies the claims: aud (audience), iss (issuer), exp (expiration)

If verification passes, the user identifier becomes what it should have been all along: a stable label you store—not a secret you trust.

⚠️ Operational Gotcha: First-Time Fields

Some fields (email, fullName) are only returned the first time a user authorizes your app. Don't build a backend flow that assumes you'll always receive everything on every sign-in.

⚠️The War Story: "Hide My Email" and the Silent Relay Drop

After login, many backends immediately send a welcome email, a receipt, or a verification message. If the user chose Hide My Email, you'll store an address like:

…@privaterelay.appleid.com

From your email provider's point of view, the message may look "sent." But Apple's relay service can still drop it silently if you haven't explicitly configured email communication.

The Fix

In the Apple Developer portal, you need to:

  1. Go to Certificates, Identifiers & Profiles
  2. Select MoreConfigure Sign in with Apple for Email Communication
  3. Register the email addresses/domains you send from

Also ensure your deliverability basics are correct:

  • SPF — Sender Policy Framework
  • DKIM — DomainKeys Identified Mail

If you skip this, a meaningful chunk of your privacy-conscious users will never receive your transactional emails—and you may not get a useful bounce to guide debugging.

📚Official Documentation & Resources

Apple Developer Documentation

🎯Key Takeaways

  • 1.Never authenticate using only credential.user—it's a stable identifier, not proof of identity. Anyone who learns it can replay it.
  • 2.Send the identityToken to your backend and verify the JWT signature against Apple's public keys. That's your source of truth.
  • 3.Validate claims: Check aud (your app), iss (Apple), and exp (expiration).
  • 4.Configure the Private Relay—if you email "Hide My Email" users, register your sender domains in the Apple Developer Portal and ensure SPF/DKIM are correct.

📱About Sandboxed

Sandboxed is a deep-dive podcast series on iOS security—built for developers who ship apps to millions of users and need to understand what's actually happening under the hood.

Each episode gives you a mental model you can apply this week—concrete enough to act on, without the 50-page spec.

If that sounds useful, I'd love to have you along.

Ready to dive deeper?

Next up: Biometrics UX—when Face ID prompts improve security, and when they just train users to blindly approve everything.

Stay in the Loop

Get iOS security insights, new episode alerts, and exclusive content delivered to your inbox.

No spam. Unsubscribe anytime.

Sign in with Apple: Threat Model and Common Mistakes | Sandboxed Podcast