Skip to content

feat: native app implementation#1871

Open
kushagrasarathe wants to merge 2 commits intodevfrom
feat/native-app
Open

feat: native app implementation#1871
kushagrasarathe wants to merge 2 commits intodevfrom
feat/native-app

Conversation

@kushagrasarathe
Copy link
Copy Markdown
Contributor

@kushagrasarathe kushagrasarathe commented Apr 3, 2026

Tasks

  • contributes to TASK-18946 : Passkeys: Shared checklist

Summary

  • adds platform detection utilities (isCapacitor, getApiBaseUrl, getPlatform) in src/utils/capacitor.ts
  • extracts native passkey signing code from poc into reusable src/utils/native-webauthn.ts
  • integrates native passkeys into production auth flow (useZeroDev.ts)
  • hardcodes rpId to peanut.me in capacitor context for passkey portability
  • on android: uses capacitor-webauthn plugin for signing via native credential manager
  • on ios: uses existing browser webauthn api (works natively in wkwebview, no plugin needed)
  • adds capacitor-aware checks in usePasskeySupport.ts and passkeyPreflight.ts

Test plan

  • web: verify passkey registration and login work unchanged
  • android native: test passkey registration + login + transaction signing via capacitor webview
  • ios native: test passkey flow via wkwebview (requires paid apple developer account for associated domains)
  • verify npx tsc --noEmit passes (no new type errors introduced)

platform detection (isCapacitor, getApiBaseUrl, getPlatform) and
native passkey signing extracted from poc into reusable modules.
- hardcode rpId to peanut.me in capacitor context
- attach native signing callback on android (uses capacitor-webauthn plugin)
- ios uses existing browser webauthn via wkwebview (no plugin needed)
- skip browser-level passkey checks in capacitor (handled natively)
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Error Error Apr 3, 2026 4:01pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 3, 2026

Walkthrough

This PR adds Capacitor support for WebAuthn/passkey flows by introducing platform detection utilities, native signing callbacks, and conditional bypass logic that forces passkey support when running under Capacitor instead of performing browser capability checks.

Changes

Cohort / File(s) Summary
New Capacitor Utilities
src/utils/capacitor.ts, src/utils/native-webauthn.ts
Introduced two new utility modules: capacitor.ts exports environment detection helpers (isCapacitor(), getPlatform(), isAndroidNative(), isIOSNative(), getApiBaseUrl(), openExternalUrl()); native-webauthn.ts provides native WebAuthn integration via public key parsing (parsePublicKeyToWebAuthnKey) and a sign message callback factory (createNativeSignMessageCallback) that routes signing through the Capacitor WebAuthn plugin with Base64URL/hex conversions and low-S ECDSA normalization.
Hook Updates
src/hooks/usePasskeySupport.ts, src/hooks/useZeroDev.ts, src/utils/passkeyPreflight.ts
Modified three files to add Capacitor-specific conditional logic: usePasskeySupport.ts forces support flags to true and skips browser checks; useZeroDev.ts derives rpId as 'peanut.me' under Capacitor and assigns native signing callback on Android; passkeyPreflight.ts returns early with hardcoded support and shortened diagnostics for Capacitor.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat: native app implementation' is partially related to the changeset. It refers to a real aspect of the change (native app support), but is overly broad and does not capture the main point that this is specifically about native passkey integration. Consider a more specific title such as 'feat: native passkey integration via capacitor' or 'feat: add passkey support for native apps' to better convey the primary focus of the changeset.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The description comprehensively relates to the changeset, detailing platform detection utilities, native passkey signing extraction, integration into production flows, rpId hardcoding, Android and iOS specific implementations, and test plans.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/native-app

Comment @coderabbitai help to get the list of available commands and usage tips.

@kushagrasarathe kushagrasarathe changed the title feat: native app passkey integration (phase 2 shared tasks) feat: native app implementation Apr 3, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/hooks/usePasskeySupport.ts`:
- Around line 34-40: The current early return in usePasskeySupport (inside the
isCapacitor() branch) force-sets setBrowserSupported(true),
setConditionalMediationSupported(true), and setIsSupported(true), which
suppresses the UnsupportedBrowserModal even when native setup is incomplete;
instead, detect native capability more conservatively: remove the unconditional
true assignments and call a validation function (or check existing context
helpers) that verifies the native plugin/associated-domain wiring (e.g., probe
the Android passkey plugin or iOS associated-domains API) and only set
setBrowserSupported, setConditionalMediationSupported, and setIsSupported to
true when that probe succeeds; otherwise set them false so passkeySupportContext
and UnsupportedBrowserModal can surface the unsupported-native setup to users.

In `@src/hooks/useZeroDev.ts`:
- Around line 89-93: Persisted WebAuthn keys lose their function-valued
signMessageCallback (so Android native signing breaks); to fix, when restoring a
stored webAuthnKey on Android native reattach the native signing callback by
calling createNativeSignMessageCallback(rpId) and assigning it to
webAuthnKey.signMessageCallback before using or saving the key; specifically,
ensure the restore/hydration code path that reads the cookie (the code in
kernelClient.context.tsx that restores WEB_AUTHN_COOKIE_KEY) and any place that
loads webAuthnKey from storage re-applies webAuthnKey.signMessageCallback =
createNativeSignMessageCallback(rpId) (and likewise do this wherever webAuthnKey
is deserialized), so the native callback persists across restarts.

In `@src/utils/capacitor.ts`:
- Around line 68-75: The bundler can fail because the dynamic import of
'@capacitor/browser' inside isCapacitor() (the try { const mod = await
import('@capacitor/browser'); await mod.Browser.open({ url }) } catch {
window.location.href = url }) still requires the package to be present at bundle
time; add "@capacitor/browser" to package.json under "optionalDependencies" (not
"dependencies") so it’s declared but non-fatal when absent, then
reinstall/update the lockfile (pnpm install / update pnpm-lock.yaml) so the
package is recorded and builds that reference the dynamic import succeed while
keeping the runtime fallback.
- Around line 6-7: The isCapacitor() function currently checks window.Capacitor
truthiness; change it to use Capacitor.isNativePlatform() instead (i.e., return
Capacitor.isNativePlatform()). Ensure the Capacitor symbol is imported (for
example import { Capacitor } from '@capacitor/core' or the existing Capacitor
export used in the project) so isCapacitor() calls Capacitor.isNativePlatform()
directly and will only return true on native iOS/Android platforms.

In `@src/utils/native-webauthn.ts`:
- Around line 184-187: The code currently decodes cred.response.clientDataJSON
with atob after only swapping -/_ which can fail due to missing padding; replace
that logic in native-webauthn by decoding cred.response.clientDataJSON with
base64URLToBytes(...) and then new TextDecoder().decode(...) to get
clientDataJSON, then search for the marker before converting to BigInt: compute
beforeType by using clientDataJSON.lastIndexOf('"type":"webauthn.get"') and
check it is >= 0 (or throw/handle) before calling BigInt(beforeType) so you
avoid invalid BigInt conversions; update any uses of atob and the previous
beforeType assignment accordingly.
- Around line 6-7: The code imports p256 from '@noble/curves' in native-webauthn
(import { p256 } from '@noble/curves/p256'), but that API was removed in v2 and
'@noble/curves' is not a direct dependency; add an explicit pinned dependency to
package.json (e.g. "@noble/curves": "^1.9.7") so the v1 API remains available
and dependency resolution won't upgrade to v2.

In `@src/utils/passkeyPreflight.ts`:
- Around line 33-45: The current isCapacitor() branch unconditionally sets
isSupported: true and green diagnostics; instead, detect actual native support
and surface a warning/false when missing: call whatever native plugin presence
check your app uses (e.g., the Capacitor passkey plugin accessor) and verify
Android plugin availability (rather than relying only on
/android/i.test(navigator.userAgent)) and iOS associated-domains configuration,
then populate diagnostics.hasPublicKeyCredential, isHttps, isAndroid, and rpId
based on those real checks and set isSupported: false and a descriptive warning
when the native plugin or associated domains are absent; keep the same
diagnostics shape so consumers like SetupPasskey.tsx can show the warning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0fed1d9f-8dcd-487e-a0d9-03b66d29c437

📥 Commits

Reviewing files that changed from the base of the PR and between 3720890 and 0fcaae5.

📒 Files selected for processing (5)
  • src/hooks/usePasskeySupport.ts
  • src/hooks/useZeroDev.ts
  • src/utils/capacitor.ts
  • src/utils/native-webauthn.ts
  • src/utils/passkeyPreflight.ts

Comment on lines +34 to +40
// in capacitor, passkeys are handled natively (android via plugin, ios via WKWebView)
// skip browser-level checks since they may report false negatives in a webview
if (isCapacitor()) {
setBrowserSupported(true)
setConditionalMediationSupported(true)
setIsSupported(true)
return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

This bypass hides unsupported native setups from the UI.

src/context/passkeySupportContext.tsx feeds these flags into src/components/Global/UnsupportedBrowserModal/index.tsx. Forcing browserSupported, conditionalMediationSupported, and isSupported to true means missing Android plugin wiring or iOS associated-domain setup will never show the modal and users only discover the problem after auth/signing fails.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/usePasskeySupport.ts` around lines 34 - 40, The current early
return in usePasskeySupport (inside the isCapacitor() branch) force-sets
setBrowserSupported(true), setConditionalMediationSupported(true), and
setIsSupported(true), which suppresses the UnsupportedBrowserModal even when
native setup is incomplete; instead, detect native capability more
conservatively: remove the unconditional true assignments and call a validation
function (or check existing context helpers) that verifies the native
plugin/associated-domain wiring (e.g., probe the Android passkey plugin or iOS
associated-domains API) and only set setBrowserSupported,
setConditionalMediationSupported, and setIsSupported to true when that probe
succeeds; otherwise set them false so passkeySupportContext and
UnsupportedBrowserModal can surface the unsupported-native setup to users.

Comment on lines +89 to +93
// on android native, attach the native signing callback so zerodev
// uses the capacitor-webauthn plugin instead of browser WebAuthn api
if (isAndroidNative()) {
webAuthnKey.signMessageCallback = createNativeSignMessageCallback(rpId)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf "== Persisted WebAuthn key readers/writers ==\n"
rg -n -C3 --type ts --type tsx '\bWEB_AUTHN_COOKIE_KEY\b|["'"'"']web-authn-key["'"'"']|\bsaveToCookie\s*\(|\bgetFromCookie\s*\('

printf "\n== Native callback attachment points ==\n"
rg -n -C3 --type ts --type tsx '\bsignMessageCallback\b|\bcreateNativeSignMessageCallback\s*\(|\bsetWebAuthnKey\s*\('

Repository: peanutprotocol/peanut-ui

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf "== Persisted WebAuthn key readers/writers ==\n"
rg -n -C3 --type ts '\bWEB_AUTHN_COOKIE_KEY\b|web-authn-key|saveToCookie\s*\(|getFromCookie\s*\('

printf "\n== Native callback attachment points ==\n"
rg -n -C3 --type ts '\bsignMessageCallback\b|createNativeSignMessageCallback|setWebAuthnKey\s*\('

Repository: peanutprotocol/peanut-ui

Length of output: 13714


🏁 Script executed:

sed -n '160,185p' src/context/kernelClient.context.tsx

Repository: peanutprotocol/peanut-ui

Length of output: 1413


🏁 Script executed:

rg -n 'isAndroidNative|signMessageCallback' src/ --type ts

Repository: peanutprotocol/peanut-ui

Length of output: 763


Android native signing callbacks will not persist across app restarts.

The signMessageCallback assigned at lines 92 and 166 is a function and will not survive the JSON serialization that occurs when the key is saved to a cookie via saveToCookie(WEB_AUTHN_COOKIE_KEY, webAuthnKey, 90). When the app restarts or the page reloads, the stored key is restored from the cookie in kernelClient.context.tsx (line 173) without reattaching the native callback. Transaction signing will then fail on Android native, forcing a fallback to the broken web path.

The callback must be reattached during hydration for any code path that restores a persisted WebAuthn key on Android native.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useZeroDev.ts` around lines 89 - 93, Persisted WebAuthn keys lose
their function-valued signMessageCallback (so Android native signing breaks); to
fix, when restoring a stored webAuthnKey on Android native reattach the native
signing callback by calling createNativeSignMessageCallback(rpId) and assigning
it to webAuthnKey.signMessageCallback before using or saving the key;
specifically, ensure the restore/hydration code path that reads the cookie (the
code in kernelClient.context.tsx that restores WEB_AUTHN_COOKIE_KEY) and any
place that loads webAuthnKey from storage re-applies
webAuthnKey.signMessageCallback = createNativeSignMessageCallback(rpId) (and
likewise do this wherever webAuthnKey is deserialized), so the native callback
persists across restarts.

Comment on lines +6 to +7
export function isCapacitor(): boolean {
return typeof window !== 'undefined' && !!(window as any).Capacitor
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Capacitor, can window.Capacitorbe defined when the app is running in a normal web browser, and isCapacitor.isNativePlatform()orCapacitor.getPlatform() !== "web" the recommended way to detect a native iOS/Android runtime?

💡 Result:

Yes, window.Capacitor is defined when running in a normal web browser, as Capacitor provides a JavaScript runtime that works across web, iOS, and Android platforms. The recommended way to detect a native iOS/Android runtime is Capacitor.isNativePlatform, which returns true for native platforms (ios/android) and false for web. Alternatively, Capacitor.getPlatform !== 'web' can be used, as getPlatform returns 'web', 'ios', or 'android'.

Citations:


🏁 Script executed:

# Check the actual implementation in src/utils/capacitor.ts
cat -n src/utils/capacitor.ts | head -20

Repository: peanutprotocol/peanut-ui

Length of output: 919


🏁 Script executed:

# Search for usages of isCapacitor function
rg "isCapacitor" --no-heading -B 2 -A 2

Repository: peanutprotocol/peanut-ui

Length of output: 3545


Use Capacitor.isNativePlatform() instead of checking window.Capacitor truthiness.

window.Capacitor is defined even in web builds when the Capacitor runtime is loaded. The current implementation will incorrectly trigger native-specific bypasses—including rpId override in passkey flows, skipped browser-level passkey checks, and API base URL overrides—on web builds. Replace with Capacitor.isNativePlatform(), which returns true only for native platforms (iOS/Android) and false for web, per the official Capacitor documentation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/capacitor.ts` around lines 6 - 7, The isCapacitor() function
currently checks window.Capacitor truthiness; change it to use
Capacitor.isNativePlatform() instead (i.e., return
Capacitor.isNativePlatform()). Ensure the Capacitor symbol is imported (for
example import { Capacitor } from '@capacitor/core' or the existing Capacitor
export used in the project) so isCapacitor() calls Capacitor.isNativePlatform()
directly and will only return true on native iOS/Android platforms.

Comment on lines +68 to +75
if (isCapacitor()) {
try {
// @ts-ignore -- @capacitor/browser may not be installed yet
const mod = await import('@capacitor/browser')
await mod.Browser.open({ url })
} catch {
// fallback if plugin not installed
window.location.href = url
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -HI '^(package\.json|pnpm-lock\.yaml|package-lock\.json|yarn\.lock)$' . -x sh -c '
  printf "\n== %s ==\n" "$1"
  rg -n -C1 "\"@capacitor/browser\"" "$1" || true
' sh {}

Repository: peanutprotocol/peanut-ui

Length of output: 113


🏁 Script executed:

# Check if the file exists and view the exact lines mentioned
head -n 75 src/utils/capacitor.ts | tail -n 20

Repository: peanutprotocol/peanut-ui

Length of output: 732


🏁 Script executed:

# View the full package.json to see all dependency sections
cat package.json | head -n 100

Repository: peanutprotocol/peanut-ui

Length of output: 3757


🏁 Script executed:

# Search for any mention of capacitor anywhere in the repo
rg -i "capacitor" --type-list | head -5
rg -i "capacitor" -l

Repository: peanutprotocol/peanut-ui

Length of output: 626


🏁 Script executed:

# Get the full package.json and check all dependency sections
cat package.json | tail -n +40

Repository: peanutprotocol/peanut-ui

Length of output: 5269


🏁 Script executed:

# Also check if `@capacitor/browser` is mentioned anywhere else in the repo
rg "@capacitor" --no-heading

Repository: peanutprotocol/peanut-ui

Length of output: 1867


Add @capacitor/browser to package.json as an optional dependency.

The dynamic import with @ts-ignore and catch block only provides runtime fallback. String-literal dynamic imports still resolve during bundling, so without @capacitor/browser declared in package.json and locked in pnpm-lock.yaml, the build can fail before the fallback executes. The package should be declared as an optionalDependency since it's conditionally used.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/capacitor.ts` around lines 68 - 75, The bundler can fail because
the dynamic import of '@capacitor/browser' inside isCapacitor() (the try { const
mod = await import('@capacitor/browser'); await mod.Browser.open({ url }) }
catch { window.location.href = url }) still requires the package to be present
at bundle time; add "@capacitor/browser" to package.json under
"optionalDependencies" (not "dependencies") so it’s declared but non-fatal when
absent, then reinstall/update the lockfile (pnpm install / update
pnpm-lock.yaml) so the package is recorded and builds that reference the dynamic
import succeed while keeping the runtime fallback.

Comment on lines +6 to +7
// @ts-ignore -- @noble/curves/p256 requires pinning to v1.9.7 (v2 removed this export)
import { p256 } from '@noble/curves/p256'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -HI '^(package\.json|pnpm-lock\.yaml|package-lock\.json|yarn\.lock)$' . -x sh -c '
  printf "\n== %s ==\n" "$1"
  rg -n -C1 "\"@noble/curves\"" "$1" || true
' sh {}

Repository: peanutprotocol/peanut-ui

Length of output: 113


Add @noble/curves as an explicit dependency pinned to 1.x in package.json.

The import at line 6 depends on an API that was removed in @noble/curves v2, but @noble/curves is not declared as a direct dependency in package.json. Without an explicit declaration and version pin, the import can break on dependency resolution changes. Add an entry like "@noble/curves": "^1.9.7" to package.json to ensure the correct API remains available and prevent runtime failures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/native-webauthn.ts` around lines 6 - 7, The code imports p256 from
'@noble/curves' in native-webauthn (import { p256 } from '@noble/curves/p256'),
but that API was removed in v2 and '@noble/curves' is not a direct dependency;
add an explicit pinned dependency to package.json (e.g. "@noble/curves":
"^1.9.7") so the v1 API remains available and dependency resolution won't
upgrade to v2.

Comment on lines +184 to +187
const clientDataJSON = atob(cred.response.clientDataJSON.replace(/-/g, '+').replace(/_/g, '/'))

// zerodev looks for '"type":"webauthn.get"' in clientDataJSON
const beforeType = BigInt(clientDataJSON.lastIndexOf('"type":"webauthn.get"'))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Decode clientDataJSON as Base64URL before searching it.

cred.response.clientDataJSON is another Base64URL field. atob() after only swapping -/_ skips padding normalization and can throw for valid payload lengths; if that happens, native signing fails before the ABI payload is built. Decode via base64URLToBytes() + TextDecoder, and guard the marker lookup before converting it to uint256.

Proposed fix
-        const clientDataJSON = atob(cred.response.clientDataJSON.replace(/-/g, '+').replace(/_/g, '/'))
-
-        // zerodev looks for '"type":"webauthn.get"' in clientDataJSON
-        const beforeType = BigInt(clientDataJSON.lastIndexOf('"type":"webauthn.get"'))
+        const clientDataJSON = new TextDecoder().decode(
+            base64URLToBytes(cred.response.clientDataJSON)
+        )
+
+        // zerodev looks for '"type":"webauthn.get"' in clientDataJSON
+        const responseTypeLocation = clientDataJSON.lastIndexOf('"type":"webauthn.get"')
+        if (responseTypeLocation < 0) {
+            throw new Error('Missing webauthn.get marker in clientDataJSON')
+        }
+        const beforeType = BigInt(responseTypeLocation)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/native-webauthn.ts` around lines 184 - 187, The code currently
decodes cred.response.clientDataJSON with atob after only swapping -/_ which can
fail due to missing padding; replace that logic in native-webauthn by decoding
cred.response.clientDataJSON with base64URLToBytes(...) and then new
TextDecoder().decode(...) to get clientDataJSON, then search for the marker
before converting to BigInt: compute beforeType by using
clientDataJSON.lastIndexOf('"type":"webauthn.get"') and check it is >= 0 (or
throw/handle) before calling BigInt(beforeType) so you avoid invalid BigInt
conversions; update any uses of atob and the previous beforeType assignment
accordingly.

Comment on lines +33 to +45
// in capacitor, passkeys are handled natively — skip browser-level preflight checks
if (isCapacitor()) {
return {
isSupported: true,
warning: null,
diagnostics: {
hasPublicKeyCredential: true,
isHttps: true,
isAndroid: /android/i.test(navigator.userAgent),
rpId: 'peanut.me',
},
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't turn Capacitor into an unconditional green light.

This now reports isSupported: true plus successful diagnostics without checking whether the Android plugin is actually available or whether iOS associated domains are configured. src/components/Setup/Views/SetupPasskey.tsx only looks at isSupported/warning, so native misconfigurations stop surfacing here and the first failure moves to registration/signing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/passkeyPreflight.ts` around lines 33 - 45, The current
isCapacitor() branch unconditionally sets isSupported: true and green
diagnostics; instead, detect actual native support and surface a warning/false
when missing: call whatever native plugin presence check your app uses (e.g.,
the Capacitor passkey plugin accessor) and verify Android plugin availability
(rather than relying only on /android/i.test(navigator.userAgent)) and iOS
associated-domains configuration, then populate
diagnostics.hasPublicKeyCredential, isHttps, isAndroid, and rpId based on those
real checks and set isSupported: false and a descriptive warning when the native
plugin or associated domains are absent; keep the same diagnostics shape so
consumers like SetupPasskey.tsx can show the warning.

@notion-workspace
Copy link
Copy Markdown

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