How to Add Sign in with Apple to Your Expo App

DavidDavid September 15, 2026 9 min read
A smartphone on a warm oak desk showing a clean dark sign-in button beside a brass key and a closed padlock in soft morning daylight, with empty space in the upper left
Original image, App Store Launch Club

Adding Sign in with Apple to Your Expo App: the Short Answer

Adding Sign in with Apple to your Expo app is four steps: install expo-apple-authentication, enable the Apple authentication capability so EAS Build provisions it, render Apple's own native button, and handle the credential the OS hands back when the user completes the flow. Expo wraps Apple's AuthenticationServices framework behind a small JavaScript module, so from your side it is one install, one config entry, one button component, and one async call. The button only appears on iOS, which is correct - Sign in with Apple is an iOS and iPadOS capability, and your Android build uses your other login methods.

The part that trips up first-time builders is not the button. It is what happens after the tap. Apple returns the user's full name and email exactly once - on the first sign-in - and returns a stable user identifier every time. If you do not persist the name and email on that first callback, they are gone, and you are left with an account that has an ID but no way to greet the person or send them anything. Get the first-sign-in capture right and the rest of the flow is standard token handling.

Step 1 - Install the Module and Enable the Capability

Install the module the Expo way so its config plugin is registered: run npx expo install expo-apple-authentication rather than a plain npm install. The expo install command pins a version compatible with your SDK and wires the config plugin into your build, which is what adds Apple's Sign in with Apple entitlement to the native project during EAS Build. Without the entitlement, the button renders but signInAsync() fails on a real device.

  1. Run npx expo install expo-apple-authentication to add the module and register its config plugin.
  2. Add 'expo-apple-authentication' to the plugins array in app.json (the module's docs list the exact plugin entry), or confirm expo install added it for you.
  3. Set ios.usesAppleSignIn to true in app.json. This is the field that tells EAS Build to enable the Sign in with Apple capability on the app identifier.
  4. Run a fresh development build or EAS Build after this change. The capability is baked into the native app, so it does not take effect on a JavaScript reload or in a generic Expo Go session.

Step 2 - Render Apple's Native Button

Apple requires you to use their own button component, not a custom-styled one, so the button looks identical to Sign in with Apple everywhere on the platform. The module ships that button as AppleAuthentication.AppleAuthenticationButton, and it exposes props for the button type (sign in, continue, sign up), the style (black, white, white with outline), and the corner radius. Match the button to your screen's background - black on light screens, white on dark - and keep it the standard shape.

Only show the button when the device actually supports Sign in with Apple. Call AppleAuthentication.isAvailableAsync() when the login screen mounts, store the result in state, and render the button only when it returns true. On Android and on older iOS versions it returns false, and you fall back to your other login methods. Rendering the Apple button on a device that cannot use it is both a bad experience and a review risk.

Step 3 - Handle the Credential You Get Back

When the user completes the flow, signInAsync() resolves with a credential object. The two fields that matter for your account system are the stable user identifier, which is the same on every sign-in and is how you recognize a returning user, and the identityToken, a signed JWT you send to your backend to verify the sign-in and mint your own session. The fullName and email fields are the ones with the catch: Apple populates them only on the first authorization.

What each field in the Apple credential is for

FieldWhen it is presentWhat you do with it
userEvery sign-in (stable per app per person)Your primary key for the account - look up or create the user by this ID
identityTokenEvery sign-inSend to your backend to verify with Apple's public keys and create your session
fullNameFirst sign-in onlyPersist it the first time; it is null on every later sign-in
emailFirst sign-in onlyPersist it the first time; may be a private relay address the user chose to share
authorizationCodeEvery sign-inSend to your backend if you need refresh tokens for server-side session revocation

The verification belongs on your backend, not in the app. Your app sends the identityToken up; your server checks the token's signature against Apple's public keys, confirms it was issued for your app, and only then creates a session. Trusting the raw credential on the device alone means anyone who can call your API can claim to be any user. Treat the app side as collecting the token and the server side as the source of truth.

Capture the Name and Email on First Sign-In

This is the single most common Sign in with Apple bug, and it is silent - everything works in testing until a real user signs in a second time. Apple returns fullName and email only on the very first authorization for your app. On every subsequent sign-in those fields are null, by design, because Apple assumes you saved them. If your account creation code only runs after the first sign-in has already happened once, you never see the name or email at all.

  1. On the credential callback, check whether fullName and email are present. If they are, this is a first sign-in - send them to your backend and store them on the account immediately.
  2. Key everything off the stable user identifier, not the email. The user ID never changes; the email can be a private relay and the user can stop sharing it later.
  3. To test this repeatedly during development, revoke your app's access under the Apple ID settings (Sign in with Apple section) on the test device. That resets the first-sign-in state so Apple sends the name and email again on the next attempt.
  4. Never require the email to be a real, deliverable address you can market to. A user who picks the private relay option gets an anonymized forwarding address, and forcing them off it is against the rules and a rejection risk.

Let Claude Code Wire the Whole Flow

The Sign in with Apple flow is mechanical and has a fixed shape, which is exactly the kind of work Claude Code handles well from the desktop app. Describe what you want - 'add Sign in with Apple to my login screen and create a session on my backend' - and it writes the availability check, the button render, the signInAsync call, the first-sign-in capture, and the backend token exchange. It also knows the app.json fields the capability needs, so it can add usesAppleSignIn and the plugin entry for you.

  1. Have Claude Code add the module, the plugin entry, and ios.usesAppleSignIn to app.json in one pass, so the capability is wired before you build.
  2. Ask it to generate the login button behind an isAvailableAsync() guard, with the correct Apple button type and a black-or-white style matched to your screen.
  3. Tell it to write the first-sign-in capture explicitly - the branch that persists fullName and email only when they are present - because that is the piece a hand-written flow most often gets wrong.
  4. Ask for the backend verification stub too: the endpoint that takes the identityToken, verifies it against Apple's public keys, and returns your session. Keep verification on the server, and let Claude Code scaffold both halves at once.

Common Sign in with Apple Mistakes and How to Avoid Them

Sign in with Apple bugs cluster around a few predictable mistakes. Most come from missing the capability, forgetting the first-sign-in data capture, or verifying the token in the wrong place.

Common Sign in with Apple mistakes in an Expo app

MistakeWhat goes wrongFix
Not setting ios.usesAppleSignIn in app.jsonThe button renders but signInAsync() fails with an authorization error on deviceSet usesAppleSignIn to true and run a fresh EAS Build so the entitlement is provisioned
Reading fullName and email on every sign-inThey are null after the first sign-in, so accounts end up with no name or emailPersist them on the first callback only, keyed off the stable user identifier
Trusting the credential on the deviceAnyone who can call your API can impersonate any userSend the identityToken to your backend and verify it against Apple's public keys before creating a session
Rendering the Apple button on unsupported devicesA button that taps into nothing on Android or old iOSGuard the render with AppleAuthentication.isAvailableAsync()
Adding Google login without AppleGuideline 4.8 rejection for offering a third-party login with no privacy-preserving equivalentOffer Sign in with Apple alongside any third-party or social login

TL;DR

Add Sign in with Apple to your Expo app by installing expo-apple-authentication, setting ios.usesAppleSignIn to true in app.json, rendering AppleAuthentication.AppleAuthenticationButton behind an isAvailableAsync() guard, and calling signInAsync() on tap. Send the identityToken to your backend and verify it against Apple's public keys before you create a session - never trust the credential on the device alone. Capture fullName and email on the first sign-in only, because Apple returns null for both on every later sign-in, and key your accounts off the stable user identifier rather than the email. If your app offers any third-party login, Guideline 4.8 requires this option too. Claude Code wires the button, the capture, and the backend exchange in one pass; you confirm the first-sign-in branch by hand, because that is the one step with no second chance.

Free app-building tips, straight to your inbox

Short, practical drops on app ideas, validating fast, store listings, and passing app review. No spam, unsubscribe anytime.

Frequently asked questions

Do I have to add Sign in with Apple to my Expo app?

Only if your app offers another third-party or social login. Guideline 4.8 requires that any app using a third-party login service (like Google or Facebook) also offers a login option that limits data collection to name and email and lets the user keep their email private. Sign in with Apple meets that bar. If your app only has its own email-and-password login, you are not required to add it, though many builders do anyway because iOS users trust it.

Why is the email null after the first Sign in with Apple sign-in?

By design. Apple returns the user's fullName and email only on the very first authorization for your app and assumes you saved them. Every later sign-in returns null for both and only gives you the stable user identifier. Persist the name and email on that first callback. To test again during development, revoke your app under the Apple ID Sign in with Apple settings on the device, which resets the first-sign-in state.

Where should I verify the Apple identity token?

On your backend, never in the app alone. Your app collects the identityToken from the credential and sends it to your server, which verifies the signature against Apple's public keys, confirms it was issued for your app, and only then creates a session. Trusting the credential on the device means anyone who can call your API can impersonate a user. The app collects; the server is the source of truth.

Does Sign in with Apple work on Android in an Expo app?

The native button is an iOS and iPadOS capability, so AppleAuthentication.isAvailableAsync() returns false on Android and the button does not render there. Guard your login screen with that availability check and fall back to your other login methods on Android. You build both platforms from one Expo codebase, so the same login screen shows Apple on iOS and your other options everywhere else.

Can I style the Sign in with Apple button myself?

No. Apple requires the standard button provided through AppleAuthentication.AppleAuthenticationButton so it looks the same across every app. You can choose the button type (sign in, continue, sign up), the style (black, white, or white with an outline), and the corner radius, and you should match black or white to your screen background, but you cannot replace it with a custom-drawn button.

Do I need EAS Build to test Sign in with Apple?

You need a build that includes the Sign in with Apple entitlement, which a development build or EAS Build with ios.usesAppleSignIn set to true provides. The generic Expo Go app does not carry your app's entitlement, so the sign-in call fails there. Make a development build of your own app and test the full flow on a real device before you submit.

Last reviewed by David on September 15, 2026

David

Written by

David

Founder and app builder

Keep reading

App StoreLaunch

App Store Phased Release: How the 7-Day Rollout Works, How to Pause It, and When to Skip It

App Store phased release rolls a version update out to a random sample of users with automatic updates on over 7 days: 1 percent on day one, then 2, 5, 10, 20, 50 and 100. You can pause it for up to 30 days in total, or release to everyone with one button. Here is exactly what Apple says it does, what it does not do, and how I decide when an Expo app update should use it.

David 8 min
Read article
Apple App ReviewApp Store

App Store Age Rating Questionnaire: How to Answer It Correctly (4+, 9+, 13+, 16+, 18+)

The App Store age rating questionnaire is the required set of questions in App Store Connect that decides whether your app shows as 4+, 9+, 13+, 16+ or 18+. Apple calculates the rating from your answers, and a handful of capability questions (unrestricted web access, social media, medical information) push a plain utility app up two tiers if you answer them loosely. Here is what each question means, which answers move the rating, and how to fill it in for an Expo app.

David 9 min
Read article

Ready to build it yourself?

Join App Store Launch Club, the #1 community for building and launching apps, for $9/month.

← Back to the blog