Skip to content

feat(mobile): react-native-passkey + expo-dev-client + EAS build config - #514

Merged
Miracle656 merged 2 commits into
Miracle656:mainfrom
collinsezedike:feat/mobile-passkey-prebuild
Jul 30, 2026
Merged

feat(mobile): react-native-passkey + expo-dev-client + EAS build config#514
Miracle656 merged 2 commits into
Miracle656:mainfrom
collinsezedike:feat/mobile-passkey-prebuild

Conversation

@collinsezedike

@collinsezedike collinsezedike commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds react-native-passkey ^3.0.0 and expo-dev-client ~5.2.5 to frontend/mobile/package.json
  • Configures app.json with bundleIdentifier (iOS) and package (Android), both required for expo prebuild to generate native projects
  • Adds associatedDomains to the iOS section (webcredentials:veil.app + ?mode=developer variant) so the OS trusts the app as an RP for passkey assertions
  • Registers expo-dev-client in the plugins array so prebuild wires in the dev launcher
  • Creates eas.json with four profiles: development (simulator), device (physical device dev client), preview (internal distribution), production (app store)

Next steps after merge

Run locally to generate native projects and trigger the first dev-client build:

cd frontend/mobile
npm install
npx expo prebuild --clean
eas build --profile device --platform ios   # or android

The generated ios/ and android/ dirs are gitignored; EAS runs prebuild in the cloud for subsequent builds.

Vercel CI checks

The three Vercel checks are failing because the Vercel integration needs authorization from the repo owner - this is unrelated to the changes in this PR.

Test plan

  • After installing the dev client on an iOS 16+ or Android 13+ device, open the app
  • Run Passkey.isSupported() in a debug console or a temporary screen - expect true
  • Attempt passkey registration: biometric sheet appears and credential is stored
  • Attempt passkey authentication: biometric sheet re-appears and assertion succeeds

Resolves #444

@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

@collinsezedike is attempting to deploy a commit to the miracle656's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jul 27, 2026

Copy link
Copy Markdown

@collinsezedike Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Miracle656 Miracle656 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right foundational piece and I want it in — expo-dev-client plus an EAS config is exactly what unblocks passkeys, since react-native-passkey can't run in Expo Go at all. The development / device build profile split is sensible, and pinning appVersionSource: local avoids the remote-versioning surprise.

Two blockers, and the first one is genuinely my call to make, not yours.

🚨 1. Bundle identifier and domain collide with #508

You declare:

"bundleIdentifier": "app.veil.mobile",
"package": "app.veil.mobile",
"associatedDomains": ["webcredentials:veil.app", "webcredentials:veil.app?mode=developer"]

#508 (deep-linking config, open right now) declares a different identity for the same app:

BUNDLE_IDENTIFIER = 'xyz.veil.wallet'
ASSOCIATED_DOMAINS = ['app.veil.xyz']

and hardcodes that into the association files it serves from the web app:

// frontend/wallet/public/.well-known/apple-app-site-association
"appIDs": ["APPLE_TEAM_ID.xyz.veil.wallet"]

// frontend/wallet/public/.well-known/assetlinks.json
"package_name": "xyz.veil.wallet"

#510's Maestro flows also target appId: xyz.veil.wallet.

These cannot both be right, and this matters more than a normal merge conflict: a bundle identifier is permanent once the app is published to the App Store or Play Store. It also has to match the webcredentials: domain for passkey assertions to verify, and the .well-known files have to be served from that same domain.

Hold off on changing anything here until I confirm the canonical values. I'm deciding between the two and will comment with the answer — I don't want you renaming twice.

2. app.json is being removed by #508

You're editing frontend/mobile/app.json, but #508 deletes it (0+/42-) and replaces it with app.config.ts, so your changes would be dropped on merge.

I'm keeping the app.config.ts migration — the argument in #508 is sound: the deep-link surface has to agree with the resolver that parses links at runtime, and static JSON can't express or document that relationship. Once the identifier question above is settled, please move your additions (bundleIdentifier / package, associatedDomains, the expo-dev-client plugin registration, icon) into app.config.ts instead.

Note that #508's config already sets bundleIdentifier and associatedDomains — but for applinks (universal links), whereas you need webcredentials (passkey RP). Those are different entries and the app needs both:

associatedDomains: [
  ...ASSOCIATED_DOMAINS.map((d) => `applinks:${d}`),
  ...ASSOCIATED_DOMAINS.map((d) => `webcredentials:${d}`),
],

That's worth coordinating with @Olorunfemi20 directly on #508 rather than resolving it as a conflict afterwards.

Note on #512

Your #512 (swap execute screen) independently adds react-native-passkey ^3.0.0 to package.json. Once this PR lands that becomes a duplicate entry — worth dropping from #512 and letting it depend on this.

Minor

eas.json has no production profile, only development and device. Fine for now given we're pre-release, but worth a follow-up before any store submission.


Sorry to park this one on a decision rather than a code fix — the identifier really does need to be settled before anyone writes it down in three places.

@collinsezedike

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review.

Bundle identifier / domain: understood - will not change anything here until you confirm the canonical values. Once confirmed, I will rename in one pass.

app.json vs app.config.ts: understood. Once the canonical values are settled, I will move bundleIdentifier, package, associatedDomains, and the expo-dev-client plugin registration into app.config.ts rather than app.json. I will coordinate with @Olorunfemi20 on #508 directly so the webcredentials entries land alongside the existing applinks entries rather than as a separate conflict.

eas.json production profile: the production profile is present in the current commit (build.production with autoIncrement: true). The review may have been looking at an earlier draft - let me know if you see something different in the diff.

Reconcile the dev-client/EAS setup with the config work that landed since:

- app.json no longer exists (Miracle656#508 moved to app.config.ts). Port the
  expo-dev-client plugin across, and add webcredentials: alongside the existing
  applinks: entry — iOS will not offer a passkey for a domain that is not
  claimed as a webcredentials service.
- Keep main's xyz.veil.wallet identifier and app.veil.xyz domain rather than
  this branch's app.veil.mobile / veil.app, so the config stays consistent with
  the .well-known files already served by the wallet.
- Drop react-native-passkey ^3.0.0. main already ships react-native-passkeys
  ^0.4.1, which is what lib/passkey.ts uses; adding the other package would put
  two WebAuthn implementations in the same app.
- expo-dev-client ~5.2.5 does not exist on the SDK 57 line (npm notarget).
  Pinned to ~57.0.10.
- Replace the personal Apple ID in eas.json with a placeholder alongside the
  other REPLACE_WITH_* values.

Cover the new webcredentials entry in appConfig.test.ts.
tsc clean; jest 8 suites / 157 tests; expo lint clean.
@Miracle656

Copy link
Copy Markdown
Owner

Merging. The dev-client and EAS setup was a genuine gap — react-native-passkeys has native code, so nothing passkey-related is testable under Expo Go, and there was no eas.json at all.

A fair amount had to be reconciled, so flagging all of it:

app.json is gone. #508 replaced it with app.config.ts, so your changes had to move there. Ported the expo-dev-client plugin across.

Added webcredentials: alongside applinks:. This is the part I'd have missed if you hadn't raised it — main only claimed applinks:app.veil.xyz, which routes https URLs into the app but does not let iOS offer a passkey for that domain. Both services are needed, and the failure mode without the second is nasty: universal links keep working while passkey registration silently doesn't, and only on a device. Added a test for it in appConfig.test.ts so it can't be dropped again.

Kept main's identifiers. This branch used app.veil.mobile / veil.app; main is on xyz.veil.wallet / app.veil.xyz, and those values are already baked into the .well-known/apple-app-site-association and assetlinks.json files the wallet serves, plus the README's adb/simctl instructions. Changing them would have broken deep linking, so I kept main's.

Dropped react-native-passkey@^3.0.0. main already ships react-native-passkeys@^0.4.1 (note the plural — different package), and lib/passkey.ts is built on it as of #544. Adding the other one would put two WebAuthn implementations in the same binary. Worth knowing: #444's note about sdk/src/webauthn.native.ts importing react-native-passkey still stands, but the SDK isn't wired into mobile yet — that's #594 — so that decision belongs there.

Version fix. expo-dev-client: ~5.2.5 doesn't resolve on the SDK 57 line:

npm error notarget In most cases you or one of your dependencies are requesting
npm error notarget a package version that doesn't exist.

That version is from an older SDK. Pinned to ~57.0.10.

eas.json. Replaced "appleId": "ezedikecollins@gmail.com" with REPLACE_WITH_APPLE_ID, matching the other REPLACE_WITH_* placeholders — that field should carry the project's App Store Connect account rather than a contributor's personal address. The rest of the profile structure (development / device / preview / production, with simulator: false on device so a real-device build is one command) is a good setup and went in unchanged.

Verified: npx expo config --type public resolves xyz.veil.wallet with both applinks:app.veil.xyz and webcredentials:app.veil.xyz; tsc --noEmit clean; jest 8 suites / 157 tests; expo lint clean; CI green.

The acceptance criterion — Passkey.isSupported() on a real iOS 16+/Android 13+ device — still needs someone to actually run a dev-client build. That can't be checked in CI, so it's worth a manual confirmation before we depend on it.

@Miracle656
Miracle656 merged commit f9f8044 into Miracle656:main Jul 30, 2026
10 of 13 checks 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.

16. react-native-passkey + dev-client

3 participants