@algorandfoundation/react-native-passkey-autofill 1.0.0-canary.19 → 1.0.0-canary.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/android/build.gradle +1 -0
- package/android/src/main/java/co/algorand/passkeyautofill/ReactNativePasskeyAutofillModule.kt +54 -26
- package/app.plugin.js +29 -1
- package/build/ReactNativePasskeyAutofillModule.d.ts +14 -3
- package/build/ReactNativePasskeyAutofillModule.d.ts.map +1 -1
- package/build/ReactNativePasskeyAutofillModule.js +20 -1
- package/build/ReactNativePasskeyAutofillModule.js.map +1 -1
- package/package.json +18 -18
- package/src/ReactNativePasskeyAutofillModule.ts +36 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
# [1.0.0-canary.20](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.19...v1.0.0-canary.20) (2026-06-10)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- **Android:** improve setting intent handling, gradle includes pickFirst for MMKV. ([27af9e4](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/27af9e470fb2d53f375422b246e3f8d72329b2c0))
|
|
6
|
+
|
|
1
7
|
# [1.0.0-canary.19](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.18...v1.0.0-canary.19) (2026-06-09)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
package/android/build.gradle
CHANGED
package/android/src/main/java/co/algorand/passkeyautofill/ReactNativePasskeyAutofillModule.kt
CHANGED
|
@@ -4,12 +4,11 @@ import expo.modules.kotlin.modules.Module
|
|
|
4
4
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
5
5
|
import co.algorand.passkeyautofill.credentials.CredentialRepository
|
|
6
6
|
import co.algorand.passkeyautofill.service.PasskeyAutofillCredentialProviderService
|
|
7
|
-
import co.algorand.passkeyautofill.service.PasskeyAutofillCredentialProviderService.Companion.KEY_LAST_INVOKED_AT_MS
|
|
8
|
-
import com.tencent.mmkv.MMKV
|
|
9
7
|
import android.content.ComponentName
|
|
10
8
|
import android.content.Context
|
|
11
9
|
import android.content.Intent
|
|
12
10
|
import android.net.Uri
|
|
11
|
+
import android.os.Build
|
|
13
12
|
import android.provider.Settings
|
|
14
13
|
import android.util.Log
|
|
15
14
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
|
@@ -159,33 +158,43 @@ class ReactNativePasskeyAutofillModule : Module() {
|
|
|
159
158
|
* Returns `true` when this app's [PasskeyAutofillCredentialProviderService]
|
|
160
159
|
* is registered as an active credential provider for the current user.
|
|
161
160
|
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* keys are `@hide` on Android 12+, so reading them from a regular app
|
|
166
|
-
* throws `SecurityException`. We therefore combine two signals:
|
|
161
|
+
* On API 34+ (UpsideDownCake) we use the official
|
|
162
|
+
* [android.credentials.CredentialManager.isEnabledCredentialProviderService]
|
|
163
|
+
* API, which reflects the user's current toggle in Settings in real time.
|
|
167
164
|
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* to it ([PasskeyAutofillCredentialProviderService.KEY_LAST_INVOKED_AT_MS]).
|
|
173
|
-
* The Credential Manager only routes requests to *enabled* providers,
|
|
174
|
-
* so a non-zero stamp is proof that we were selected at least once.
|
|
165
|
+
* On older devices we fall back to a best-effort read of the `@hide`
|
|
166
|
+
* `credential_service` / `credential_service_primary` Secure settings. These
|
|
167
|
+
* typically throw `SecurityException` for non-system apps on Android 12+,
|
|
168
|
+
* in which case we conservatively return `false` rather than guess.
|
|
175
169
|
*/
|
|
176
170
|
private fun isProviderEnabled(context: Context): Boolean {
|
|
177
|
-
val
|
|
171
|
+
val component = ComponentName(
|
|
178
172
|
context.packageName,
|
|
179
173
|
PasskeyAutofillCredentialProviderService::class.java.name,
|
|
180
|
-
)
|
|
174
|
+
)
|
|
175
|
+
val expected = component.flattenToString()
|
|
176
|
+
|
|
177
|
+
// Preferred path (API 34+, UpsideDownCake): ask the platform
|
|
178
|
+
// `CredentialManager` system service whether our component is enabled.
|
|
179
|
+
// This is the official, real-time-accurate signal and is trusted
|
|
180
|
+
// exclusively on supported OS versions.
|
|
181
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
182
|
+
return try {
|
|
183
|
+
val cm = context.getSystemService(android.credentials.CredentialManager::class.java)
|
|
184
|
+
cm != null && cm.isEnabledCredentialProviderService(component)
|
|
185
|
+
} catch (e: Throwable) {
|
|
186
|
+
Log.d("ReactNativePasskeyAutofill", "CredentialManager.isEnabledCredentialProviderService failed: ${e.message}")
|
|
187
|
+
false
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Pre-API-34 best-effort: try the (often `@hide`) Secure settings keys.
|
|
181
192
|
val resolver = context.contentResolver
|
|
182
193
|
val keys = arrayOf("credential_service", "credential_service_primary")
|
|
183
194
|
for (key in keys) {
|
|
184
195
|
val value = try {
|
|
185
196
|
Settings.Secure.getString(resolver, key)
|
|
186
197
|
} catch (e: SecurityException) {
|
|
187
|
-
// `credential_service[_primary]` are @hide on Android 12+; fall
|
|
188
|
-
// through to the MMKV stamp check below.
|
|
189
198
|
Log.d("ReactNativePasskeyAutofill", "Secure key $key not readable: ${e.message}")
|
|
190
199
|
null
|
|
191
200
|
} ?: continue
|
|
@@ -194,14 +203,7 @@ class ReactNativePasskeyAutofillModule : Module() {
|
|
|
194
203
|
value.contains(expected, ignoreCase = true)
|
|
195
204
|
if (enabled) return true
|
|
196
205
|
}
|
|
197
|
-
|
|
198
|
-
return try {
|
|
199
|
-
MMKV.initialize(context)
|
|
200
|
-
(MMKV.defaultMMKV()?.decodeLong(KEY_LAST_INVOKED_AT_MS, 0L) ?: 0L) > 0L
|
|
201
|
-
} catch (e: Exception) {
|
|
202
|
-
Log.w("ReactNativePasskeyAutofill", "Failed to read provider stamp: ${e.message}")
|
|
203
|
-
false
|
|
204
|
-
}
|
|
206
|
+
return false
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
/**
|
|
@@ -210,8 +212,34 @@ class ReactNativePasskeyAutofillModule : Module() {
|
|
|
210
212
|
* the credential provider screen is not available on the device.
|
|
211
213
|
*/
|
|
212
214
|
private fun openCredentialProviderSettings(context: Context): Boolean {
|
|
215
|
+
// The system action for the Credential Manager provider picker is
|
|
216
|
+
// `android.settings.CREDENTIAL_PROVIDER` (`Settings.ACTION_CREDENTIAL_PROVIDER`,
|
|
217
|
+
// API 34+). Some OEM Settings builds additionally accept a
|
|
218
|
+
// `:settings:fragment_args_key` extra so the screen scrolls directly to
|
|
219
|
+
// our app's row instead of the generic list. We also include a legacy
|
|
220
|
+
// autofill-service picker fallback for devices where the Credential
|
|
221
|
+
// Manager screen isn't a directly launchable activity.
|
|
222
|
+
val component = ComponentName(
|
|
223
|
+
context.packageName,
|
|
224
|
+
PasskeyAutofillCredentialProviderService::class.java.name,
|
|
225
|
+
).flattenToString()
|
|
213
226
|
val intents = listOf(
|
|
227
|
+
// Preferred: Credential Manager provider settings, deep-linked to our row.
|
|
228
|
+
Intent("android.settings.CREDENTIAL_PROVIDER").apply {
|
|
229
|
+
putExtra(":settings:fragment_args_key", component)
|
|
230
|
+
putExtra(
|
|
231
|
+
":settings:show_fragment_args",
|
|
232
|
+
android.os.Bundle().apply { putString(":settings:fragment_args_key", component) },
|
|
233
|
+
)
|
|
234
|
+
},
|
|
235
|
+
// Same screen without the deep-link extras (some OEMs ignore them).
|
|
214
236
|
Intent("android.settings.CREDENTIAL_PROVIDER"),
|
|
237
|
+
// Legacy / fallback: the system autofill provider picker, which on
|
|
238
|
+
// pre-14 devices is the closest "pick a passkey provider" screen.
|
|
239
|
+
Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE).apply {
|
|
240
|
+
data = Uri.parse("package:${context.packageName}")
|
|
241
|
+
},
|
|
242
|
+
// Last-resort fallback: this app's details page.
|
|
215
243
|
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
|
216
244
|
data = Uri.fromParts("package", context.packageName, null)
|
|
217
245
|
},
|
package/app.plugin.js
CHANGED
|
@@ -3,6 +3,7 @@ const {
|
|
|
3
3
|
withEntitlementsPlist,
|
|
4
4
|
withInfoPlist,
|
|
5
5
|
withStringsXml,
|
|
6
|
+
withAppBuildGradle,
|
|
6
7
|
withProjectBuildGradle,
|
|
7
8
|
withMainApplication,
|
|
8
9
|
withDangerousMod,
|
|
@@ -718,7 +719,34 @@ const withPasskeyAutofill = (config, props = {}) => {
|
|
|
718
719
|
return config;
|
|
719
720
|
});
|
|
720
721
|
|
|
721
|
-
// 3.
|
|
722
|
+
// 3. Inject `pickFirst '**/libmmkv.so'` into the consumer app's
|
|
723
|
+
// `android/app/build.gradle`. The native `libmmkv.so` is bundled by both
|
|
724
|
+
// this module (via `io.github.zhongwuzw:mmkv`) and `react-native-mmkv`
|
|
725
|
+
// (which also depends on the same artifact, sometimes pinned to a different
|
|
726
|
+
// version). When two copies end up on the merged-libs path, AGP fails
|
|
727
|
+
// `mergeDebugNativeLibs` with: "2 files found with path
|
|
728
|
+
// 'lib/<abi>/libmmkv.so'". Picking the first occurrence is safe because
|
|
729
|
+
// the `com.tencent.mmkv.MMKV` Java surface we touch is ABI-stable across
|
|
730
|
+
// the 2.x range.
|
|
731
|
+
config = withAppBuildGradle(config, (config) => {
|
|
732
|
+
const marker = "pickFirst '**/libmmkv.so'";
|
|
733
|
+
if (config.modResults.contents.includes(marker)) {
|
|
734
|
+
return config;
|
|
735
|
+
}
|
|
736
|
+
let contents = config.modResults.contents;
|
|
737
|
+
if (/packagingOptions\s*\{/.test(contents)) {
|
|
738
|
+
contents = contents.replace(/packagingOptions\s*\{/, `packagingOptions {\n ${marker}`);
|
|
739
|
+
} else if (/android\s*\{/.test(contents)) {
|
|
740
|
+
contents = contents.replace(
|
|
741
|
+
/android\s*\{/,
|
|
742
|
+
`android {\n packagingOptions {\n ${marker}\n }\n`,
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
config.modResults.contents = contents;
|
|
746
|
+
return config;
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
// 4. Add local Maven repository for local AAR
|
|
722
750
|
config = withProjectBuildGradle(config, (config) => {
|
|
723
751
|
if (config.modResults.contents.includes("android/libs/repo")) {
|
|
724
752
|
return config;
|
|
@@ -7,14 +7,25 @@ declare class ReactNativePasskeyAutofillModule extends NativeModule<ReactNativeP
|
|
|
7
7
|
configureIntentActions(getPasskeyAction: string, createPasskeyAction: string): Promise<void>;
|
|
8
8
|
clearCredentials(): Promise<void>;
|
|
9
9
|
deleteCredential(credentialId: string): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* iOS: returns the identities currently published to the AutoFill
|
|
12
|
+
* `ASCredentialIdentityStore`. Android: no-op returning `[]` because the
|
|
13
|
+
* native Credential Provider service reads credentials directly from MMKV
|
|
14
|
+
* and there is no separate identity store to query.
|
|
15
|
+
*/
|
|
10
16
|
getStoredCredentials(): Promise<PasskeyAutofillCredentialIdentity[]>;
|
|
17
|
+
/**
|
|
18
|
+
* iOS: returns diagnostic strings from the AutoFill extension.
|
|
19
|
+
* Android: no-op returning `[]`.
|
|
20
|
+
*/
|
|
11
21
|
getDiagnostics(): Promise<string[]>;
|
|
12
22
|
/**
|
|
13
23
|
* Replaces the iOS AutoFill passkey identity store with credentials
|
|
14
24
|
* available to the native Credential Provider extension. Each item must
|
|
15
25
|
* include a credential id, relying party (`relyingPartyIdentifier`,
|
|
16
26
|
* `rpId`, or `origin`), user handle, and P-256 private key material.
|
|
17
|
-
* Android ignores this method because the provider reads MMKV
|
|
27
|
+
* Android ignores this method (no-op) because the provider reads MMKV
|
|
28
|
+
* directly.
|
|
18
29
|
*/
|
|
19
30
|
replaceCredentialIdentities(credentials: PasskeyAutofillCredentialIdentity[]): Promise<void>;
|
|
20
31
|
refreshCredentialIdentities(): Promise<void>;
|
|
@@ -33,6 +44,6 @@ declare class ReactNativePasskeyAutofillModule extends NativeModule<ReactNativeP
|
|
|
33
44
|
*/
|
|
34
45
|
openProviderSettings(): Promise<boolean>;
|
|
35
46
|
}
|
|
36
|
-
declare const
|
|
37
|
-
export default
|
|
47
|
+
declare const nativeModule: ReactNativePasskeyAutofillModule;
|
|
48
|
+
export default nativeModule;
|
|
38
49
|
//# sourceMappingURL=ReactNativePasskeyAutofillModule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactNativePasskeyAutofillModule.d.ts","sourceRoot":"","sources":["../src/ReactNativePasskeyAutofillModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ReactNativePasskeyAutofillModule.d.ts","sourceRoot":"","sources":["../src/ReactNativePasskeyAutofillModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAGzD,OAAO,EACL,iCAAiC,EACjC,sCAAsC,EACvC,MAAM,oCAAoC,CAAC;AAE5C,OAAO,OAAO,gCAAiC,SAAQ,YAAY,CAAC,sCAAsC,CAAC;IACzG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3C,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACzC,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IACxC,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC5F,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IACjC,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACrD;;;;;OAKG;IACH,oBAAoB,IAAI,OAAO,CAAC,iCAAiC,EAAE,CAAC;IACpE;;;OAGG;IACH,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IACnC;;;;;;;OAOG;IACH,2BAA2B,CAAC,WAAW,EAAE,iCAAiC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAC5F,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC;IAC5C;;;;;;OAMG;IACH,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IACpC;;;;OAIG;IACH,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;CACzC;AAGD,QAAA,MAAM,YAAY,kCAEjB,CAAC;AAoBF,eAAe,YAAY,CAAC"}
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { requireNativeModule } from "expo";
|
|
2
|
+
import { Platform } from "react-native";
|
|
2
3
|
// This call loads the native module object from the JSI.
|
|
3
|
-
|
|
4
|
+
const nativeModule = requireNativeModule("ReactNativePasskeyAutofill");
|
|
5
|
+
// A handful of methods are only implemented in the iOS native module because
|
|
6
|
+
// they interact with `ASCredentialIdentityStore`, which has no Android
|
|
7
|
+
// equivalent (the Android Credential Provider service reads credentials
|
|
8
|
+
// directly from MMKV on each request).
|
|
9
|
+
if (Platform.OS === "android") {
|
|
10
|
+
const noops = {
|
|
11
|
+
replaceCredentialIdentities: () => Promise.resolve(),
|
|
12
|
+
refreshCredentialIdentities: () => Promise.resolve(),
|
|
13
|
+
getStoredCredentials: () => Promise.resolve([]),
|
|
14
|
+
getDiagnostics: () => Promise.resolve([]),
|
|
15
|
+
};
|
|
16
|
+
for (const name of Object.keys(noops)) {
|
|
17
|
+
if (typeof nativeModule[name] !== "function") {
|
|
18
|
+
nativeModule[name] = noops[name];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export default nativeModule;
|
|
4
23
|
//# sourceMappingURL=ReactNativePasskeyAutofillModule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactNativePasskeyAutofillModule.js","sourceRoot":"","sources":["../src/ReactNativePasskeyAutofillModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ReactNativePasskeyAutofillModule.js","sourceRoot":"","sources":["../src/ReactNativePasskeyAutofillModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAoDxC,yDAAyD;AACzD,MAAM,YAAY,GAAG,mBAAmB,CACtC,4BAA4B,CAC7B,CAAC;AAEF,6EAA6E;AAC7E,uEAAuE;AACvE,wEAAwE;AACxE,uCAAuC;AACvC,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;IAC9B,MAAM,KAAK,GAA6D;QACtE,2BAA2B,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;QACpD,2BAA2B,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;QACpD,oBAAoB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,cAAc,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;KAC1C,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,IAAI,OAAQ,YAAmD,CAAC,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;YACpF,YAAmD,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;AACH,CAAC;AAED,eAAe,YAAY,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\nimport { Platform } from \"react-native\";\n\nimport {\n PasskeyAutofillCredentialIdentity,\n ReactNativePasskeyAutofillModuleEvents,\n} from \"./ReactNativePasskeyAutofill.types\";\n\ndeclare class ReactNativePasskeyAutofillModule extends NativeModule<ReactNativePasskeyAutofillModuleEvents> {\n setMasterKey(secret: string): Promise<void>;\n setHdRootKeyId(id: string): Promise<void>;\n getHdRootKeyId(): Promise<string | null>;\n configureIntentActions(getPasskeyAction: string, createPasskeyAction: string): Promise<void>;\n clearCredentials(): Promise<void>;\n deleteCredential(credentialId: string): Promise<void>;\n /**\n * iOS: returns the identities currently published to the AutoFill\n * `ASCredentialIdentityStore`. Android: no-op returning `[]` because the\n * native Credential Provider service reads credentials directly from MMKV\n * and there is no separate identity store to query.\n */\n getStoredCredentials(): Promise<PasskeyAutofillCredentialIdentity[]>;\n /**\n * iOS: returns diagnostic strings from the AutoFill extension.\n * Android: no-op returning `[]`.\n */\n getDiagnostics(): Promise<string[]>;\n /**\n * Replaces the iOS AutoFill passkey identity store with credentials\n * available to the native Credential Provider extension. Each item must\n * include a credential id, relying party (`relyingPartyIdentifier`,\n * `rpId`, or `origin`), user handle, and P-256 private key material.\n * Android ignores this method (no-op) because the provider reads MMKV\n * directly.\n */\n replaceCredentialIdentities(credentials: PasskeyAutofillCredentialIdentity[]): Promise<void>;\n refreshCredentialIdentities(): Promise<void>;\n /**\n * Resolves to `true` when this app is registered as the active\n * credential/autofill provider on the current device (Android 14+\n * credential provider or iOS AutoFill). Useful for gating passkey UI\n * and for E2E tests that need to confirm the prompt that appears\n * belongs to this provider and not a third party.\n */\n isProviderActive(): Promise<boolean>;\n /**\n * Opens the OS credential/autofill provider settings screen so the\n * user can enable this app as the active provider. Resolves to `true`\n * if a settings screen could be launched, `false` otherwise.\n */\n openProviderSettings(): Promise<boolean>;\n}\n\n// This call loads the native module object from the JSI.\nconst nativeModule = requireNativeModule<ReactNativePasskeyAutofillModule>(\n \"ReactNativePasskeyAutofill\",\n);\n\n// A handful of methods are only implemented in the iOS native module because\n// they interact with `ASCredentialIdentityStore`, which has no Android\n// equivalent (the Android Credential Provider service reads credentials\n// directly from MMKV on each request).\nif (Platform.OS === \"android\") {\n const noops: Record<string, (...args: unknown[]) => Promise<unknown>> = {\n replaceCredentialIdentities: () => Promise.resolve(),\n refreshCredentialIdentities: () => Promise.resolve(),\n getStoredCredentials: () => Promise.resolve([]),\n getDiagnostics: () => Promise.resolve([]),\n };\n for (const name of Object.keys(noops)) {\n if (typeof (nativeModule as unknown as Record<string, unknown>)[name] !== \"function\") {\n (nativeModule as unknown as Record<string, unknown>)[name] = noops[name];\n }\n }\n}\n\nexport default nativeModule;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@algorandfoundation/react-native-passkey-autofill",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.20",
|
|
4
4
|
"description": "Passkey AutoFill React Native",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"@algorandfoundation/react-native-passkey-autofill",
|
|
@@ -51,36 +51,36 @@
|
|
|
51
51
|
"@algorandfoundation/react-native-keystore": "1.0.0-canary.12"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@expo/config-plugins": "~55.0.
|
|
54
|
+
"@expo/config-plugins": "~55.0.10",
|
|
55
55
|
"@expo/npm-proofread": "^1.0.1",
|
|
56
56
|
"@semantic-release/changelog": "^6.0.3",
|
|
57
57
|
"@semantic-release/exec": "^7.1.0",
|
|
58
58
|
"@semantic-release/git": "^10.0.1",
|
|
59
|
-
"@semantic-release/github": "^12.0.
|
|
59
|
+
"@semantic-release/github": "^12.0.8",
|
|
60
60
|
"@semantic-release/npm": "^13.1.5",
|
|
61
61
|
"@types/jest": "^29.5.14",
|
|
62
|
-
"@types/node": "^22.19.
|
|
63
|
-
"@types/react": "~19.2.
|
|
62
|
+
"@types/node": "^22.19.20",
|
|
63
|
+
"@types/react": "~19.2.16",
|
|
64
64
|
"appium": "^2.19.0",
|
|
65
65
|
"appium-uiautomator2-driver": "^3.10.0",
|
|
66
66
|
"appium-xcuitest-driver": "^9.10.5",
|
|
67
67
|
"eslint": "^9.39.4",
|
|
68
|
-
"expo": "
|
|
69
|
-
"expo-doctor": "^1.
|
|
68
|
+
"expo": "55.0.17",
|
|
69
|
+
"expo-doctor": "^1.19.8",
|
|
70
70
|
"expo-module-scripts": "^55.0.2",
|
|
71
71
|
"jest": "^29.7.0",
|
|
72
72
|
"lefthook": "2.1.4",
|
|
73
73
|
"oxfmt": "^0.43.0",
|
|
74
|
-
"oxlint": "^1.
|
|
74
|
+
"oxlint": "^1.68.0",
|
|
75
75
|
"prettier": "^3.8.3",
|
|
76
76
|
"react": "19.2.0",
|
|
77
77
|
"react-native": "0.83.6",
|
|
78
78
|
"semantic-release": "^25.0.3",
|
|
79
79
|
"start-server-and-test": "^2.1.5",
|
|
80
|
-
"ts-jest": "^29.4.
|
|
80
|
+
"ts-jest": "^29.4.11",
|
|
81
81
|
"ts-node": "^10.9.2",
|
|
82
82
|
"typescript": "~5.9.3",
|
|
83
|
-
"webdriverio": "^9.27.
|
|
83
|
+
"webdriverio": "^9.27.2"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"expo": "*",
|
|
@@ -114,26 +114,26 @@
|
|
|
114
114
|
"react-native-quick-crypto": "1.0.19",
|
|
115
115
|
"expo": "55.0.17",
|
|
116
116
|
"ajv@>=7.0.0-alpha.0 <8.18.0": ">=8.18.0",
|
|
117
|
-
"axios@>=1.0.0 <1.
|
|
118
|
-
"axios@>=1.0.0 <1.15.0": ">=1.15.0",
|
|
119
|
-
"axios@>=1.0.0 <=1.13.4": ">=1.13.5",
|
|
117
|
+
"axios@>=1.0.0 <1.16.0": ">=1.16.0",
|
|
120
118
|
"basic-ftp@<=5.2.2": ">=5.3.0",
|
|
119
|
+
"brace-expansion@>=5.0.0 <5.0.6": ">=5.0.6",
|
|
121
120
|
"diff@>=6.0.0 <8.0.3": ">=8.0.3",
|
|
121
|
+
"fast-uri@<=3.1.1": ">=3.1.2",
|
|
122
122
|
"fast-xml-parser@<5.7.0": ">=5.7.0",
|
|
123
123
|
"form-data@>=4.0.0 <4.0.4": ">=4.0.4",
|
|
124
124
|
"glob@>=10.2.0 <10.5.0": ">=10.5.0",
|
|
125
125
|
"lodash@<=4.17.23": ">=4.18.0",
|
|
126
|
-
"lodash@>=4.0.0 <=4.17.22": ">=4.17.23",
|
|
127
|
-
"lodash@>=4.0.0 <=4.17.23": ">=4.18.0",
|
|
128
126
|
"on-headers@<1.1.0": ">=1.1.0",
|
|
129
127
|
"path-to-regexp@<0.1.13": "0.1.13",
|
|
130
128
|
"path-to-regexp@>=8.0.0 <8.4.0": ">=8.4.0",
|
|
131
129
|
"postcss@<8.5.10": ">=8.5.10",
|
|
132
|
-
"qs@<6.
|
|
133
|
-
"
|
|
130
|
+
"qs@<6.15.2": ">=6.15.2",
|
|
131
|
+
"shell-quote@>=1.1.0 <=1.8.3": ">=1.8.4",
|
|
134
132
|
"uuid@<14.0.0": ">=14.0.0",
|
|
133
|
+
"ws@>=8.0.0 <8.20.1": ">=8.20.1",
|
|
135
134
|
"yaml@>=2.0.0 <2.8.3": ">=2.8.3",
|
|
136
|
-
"yauzl@=3.2.0": ">=3.2.1"
|
|
135
|
+
"yauzl@=3.2.0": ">=3.2.1",
|
|
136
|
+
"@babel/plugin-transform-modules-systemjs@>=7.12.0 <=7.29.3": ">=7.29.4"
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NativeModule, requireNativeModule } from "expo";
|
|
2
|
+
import { Platform } from "react-native";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
PasskeyAutofillCredentialIdentity,
|
|
@@ -12,14 +13,25 @@ declare class ReactNativePasskeyAutofillModule extends NativeModule<ReactNativeP
|
|
|
12
13
|
configureIntentActions(getPasskeyAction: string, createPasskeyAction: string): Promise<void>;
|
|
13
14
|
clearCredentials(): Promise<void>;
|
|
14
15
|
deleteCredential(credentialId: string): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* iOS: returns the identities currently published to the AutoFill
|
|
18
|
+
* `ASCredentialIdentityStore`. Android: no-op returning `[]` because the
|
|
19
|
+
* native Credential Provider service reads credentials directly from MMKV
|
|
20
|
+
* and there is no separate identity store to query.
|
|
21
|
+
*/
|
|
15
22
|
getStoredCredentials(): Promise<PasskeyAutofillCredentialIdentity[]>;
|
|
23
|
+
/**
|
|
24
|
+
* iOS: returns diagnostic strings from the AutoFill extension.
|
|
25
|
+
* Android: no-op returning `[]`.
|
|
26
|
+
*/
|
|
16
27
|
getDiagnostics(): Promise<string[]>;
|
|
17
28
|
/**
|
|
18
29
|
* Replaces the iOS AutoFill passkey identity store with credentials
|
|
19
30
|
* available to the native Credential Provider extension. Each item must
|
|
20
31
|
* include a credential id, relying party (`relyingPartyIdentifier`,
|
|
21
32
|
* `rpId`, or `origin`), user handle, and P-256 private key material.
|
|
22
|
-
* Android ignores this method because the provider reads MMKV
|
|
33
|
+
* Android ignores this method (no-op) because the provider reads MMKV
|
|
34
|
+
* directly.
|
|
23
35
|
*/
|
|
24
36
|
replaceCredentialIdentities(credentials: PasskeyAutofillCredentialIdentity[]): Promise<void>;
|
|
25
37
|
refreshCredentialIdentities(): Promise<void>;
|
|
@@ -40,4 +52,26 @@ declare class ReactNativePasskeyAutofillModule extends NativeModule<ReactNativeP
|
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
// This call loads the native module object from the JSI.
|
|
43
|
-
|
|
55
|
+
const nativeModule = requireNativeModule<ReactNativePasskeyAutofillModule>(
|
|
56
|
+
"ReactNativePasskeyAutofill",
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// A handful of methods are only implemented in the iOS native module because
|
|
60
|
+
// they interact with `ASCredentialIdentityStore`, which has no Android
|
|
61
|
+
// equivalent (the Android Credential Provider service reads credentials
|
|
62
|
+
// directly from MMKV on each request).
|
|
63
|
+
if (Platform.OS === "android") {
|
|
64
|
+
const noops: Record<string, (...args: unknown[]) => Promise<unknown>> = {
|
|
65
|
+
replaceCredentialIdentities: () => Promise.resolve(),
|
|
66
|
+
refreshCredentialIdentities: () => Promise.resolve(),
|
|
67
|
+
getStoredCredentials: () => Promise.resolve([]),
|
|
68
|
+
getDiagnostics: () => Promise.resolve([]),
|
|
69
|
+
};
|
|
70
|
+
for (const name of Object.keys(noops)) {
|
|
71
|
+
if (typeof (nativeModule as unknown as Record<string, unknown>)[name] !== "function") {
|
|
72
|
+
(nativeModule as unknown as Record<string, unknown>)[name] = noops[name];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default nativeModule;
|