@algorandfoundation/react-native-passkey-autofill 1.0.0-canary.2 → 1.0.0-canary.21

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.
Files changed (66) hide show
  1. package/.nvmrc +1 -0
  2. package/.oxfmtrc.json +4 -0
  3. package/.oxlintrc.json +14 -0
  4. package/.releaserc +30 -0
  5. package/ARCHITECTURE.md +8 -0
  6. package/CHANGELOG.md +115 -0
  7. package/CONTRIBUTING.md +15 -5
  8. package/README.md +150 -11
  9. package/android/build.gradle +33 -10
  10. package/android/libs/repo/co/algorand/dP256Android/1.0.0/dP256Android-1.0.0.pom +9 -0
  11. package/android/src/main/AndroidManifest.xml +9 -3
  12. package/android/src/main/java/co/algorand/passkeyautofill/CreatePasskeyActivity.kt +396 -44
  13. package/android/src/main/java/co/algorand/passkeyautofill/GetPasskeyActivity.kt +583 -44
  14. package/android/src/main/java/co/algorand/passkeyautofill/Prf.kt +184 -0
  15. package/android/src/main/java/co/algorand/passkeyautofill/ReactNativePasskeyAutofillModule.kt +190 -2
  16. package/android/src/main/java/co/algorand/passkeyautofill/WebAuthn.kt +135 -0
  17. package/android/src/main/java/co/algorand/passkeyautofill/auth/BiometricRequirement.kt +86 -0
  18. package/android/src/main/java/co/algorand/passkeyautofill/credentials/Credential.kt +2 -1
  19. package/android/src/main/java/co/algorand/passkeyautofill/credentials/CredentialRepository.kt +314 -53
  20. package/android/src/main/java/co/algorand/passkeyautofill/service/PasskeyAutofillCredentialProviderService.kt +152 -38
  21. package/android/src/main/java/co/algorand/passkeyautofill/utils/PasskeyUtils.kt +119 -0
  22. package/android/src/main/res/values/styles.xml +10 -0
  23. package/app.plugin.js +593 -60
  24. package/assets/banner.png +0 -0
  25. package/build/ReactNativePasskeyAutofill.types.d.ts +41 -1
  26. package/build/ReactNativePasskeyAutofill.types.d.ts.map +1 -1
  27. package/build/ReactNativePasskeyAutofill.types.js +3 -1
  28. package/build/ReactNativePasskeyAutofill.types.js.map +1 -1
  29. package/build/ReactNativePasskeyAutofillModule.d.ts +41 -4
  30. package/build/ReactNativePasskeyAutofillModule.d.ts.map +1 -1
  31. package/build/ReactNativePasskeyAutofillModule.js +21 -2
  32. package/build/ReactNativePasskeyAutofillModule.js.map +1 -1
  33. package/build/index.d.ts +2 -2
  34. package/build/index.d.ts.map +1 -1
  35. package/build/index.js +2 -2
  36. package/build/index.js.map +1 -1
  37. package/e2e/README.md +155 -0
  38. package/e2e/driver/adb.ts +73 -0
  39. package/e2e/driver/capabilities.ts +106 -0
  40. package/e2e/driver/credentialProvider.ts +104 -0
  41. package/e2e/driver/fingerprint.ts +885 -0
  42. package/e2e/driver/selectors.ts +103 -0
  43. package/e2e/driver/session.ts +25 -0
  44. package/e2e/jest.config.ts +20 -0
  45. package/e2e/package.json +11 -0
  46. package/e2e/tests/passkey.test.ts +190 -0
  47. package/e2e/tsconfig.json +16 -0
  48. package/ios/AutofillCredentialProvider/BiometricRequirement.swift +34 -0
  49. package/ios/AutofillCredentialProvider/CredentialProviderViewController.swift +570 -0
  50. package/ios/AutofillCredentialProvider/PasskeyAutofillCredentialProvider-Bridging-Header.h +1 -0
  51. package/ios/AutofillCredentialProvider/PasskeyCredentialStore.swift +612 -0
  52. package/ios/AutofillCredentialProvider/PasskeyKeystoreMMKV.h +25 -0
  53. package/ios/AutofillCredentialProvider/PasskeyKeystoreMMKV.mm +86 -0
  54. package/ios/AutofillCredentialProvider/Prf.swift +89 -0
  55. package/ios/AutofillCredentialProvider/WebAuthn.swift +186 -0
  56. package/ios/ReactNativePasskeyAutofill.podspec +11 -2
  57. package/ios/ReactNativePasskeyAutofillModule.swift +170 -15
  58. package/lefthook.yml +14 -0
  59. package/package.json +115 -25
  60. package/pnpm-workspace.yaml +36 -0
  61. package/src/ReactNativePasskeyAutofill.types.ts +38 -0
  62. package/src/ReactNativePasskeyAutofillModule.ts +66 -3
  63. package/src/index.ts +2 -2
  64. package/tsconfig.json +2 -2
  65. package/.eslintrc.js +0 -5
  66. /package/android/libs/{dP256Android-release.aar → repo/co/algorand/dP256Android/1.0.0/dP256Android-1.0.0.aar} +0 -0
package/package.json CHANGED
@@ -1,49 +1,139 @@
1
1
  {
2
2
  "name": "@algorandfoundation/react-native-passkey-autofill",
3
- "version": "1.0.0-canary.2",
3
+ "version": "1.0.0-canary.21",
4
4
  "description": "Passkey AutoFill React Native",
5
+ "keywords": [
6
+ "@algorandfoundation/react-native-passkey-autofill",
7
+ "ReactNativePasskeyAutofill",
8
+ "expo",
9
+ "react-native"
10
+ ],
11
+ "homepage": "https://github.com/algorandfoundation/react-native-passkey-autofill#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/algorandfoundation/react-native-passkey-autofill/issues"
14
+ },
15
+ "license": "Apache-2.0",
16
+ "author": "Algorand Foundation",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+ssh://git@github.com/algorandfoundation/react-native-passkey-autofill.git"
20
+ },
5
21
  "main": "build/index.js",
6
22
  "types": "build/index.d.ts",
7
23
  "publishConfig": {
8
- "access": "public"
24
+ "access": "public",
25
+ "provenance": true
9
26
  },
10
27
  "scripts": {
11
28
  "build": "expo-module build",
12
29
  "clean": "expo-module clean",
13
- "lint": "expo-module lint",
14
- "test": "expo-module test",
15
- "prepare": "expo-module prepare",
30
+ "lint": "oxlint",
31
+ "lint:fix": "oxlint --fix",
32
+ "fmt": "oxfmt",
33
+ "fmt:check": "oxfmt --check",
34
+ "test": "CI=true expo-module test",
35
+ "test:android": "cd example/android && ./gradlew :algorandfoundation-react-native-passkey-autofill:testDebugUnitTest --no-daemon",
36
+ "test:android:instrumented": "cd example/android && ./gradlew :algorandfoundation-react-native-passkey-autofill:connectedDebugAndroidTest --no-daemon",
37
+ "test:all": "pnpm run test && pnpm run test:android",
16
38
  "prepublishOnly": "expo-module prepublishOnly",
17
- "expo-module": "expo-module",
18
39
  "open:ios": "xed example/ios",
19
- "open:android": "open -a \"Android Studio\" example/android"
20
- },
21
- "keywords": [
22
- "react-native",
23
- "expo",
24
- "@algorandfoundation/react-native-passkey-autofill",
25
- "ReactNativePasskeyAutofill"
26
- ],
27
- "repository": "https://github.com/algorandfoundation/react-native-passkey-autofill",
28
- "bugs": {
29
- "url": "https://github.com/algorandfoundation/react-native-passkey-autofill/issues"
40
+ "open:android": "open -a \"Android Studio\" example/android",
41
+ "appium:start": "appium --base-path / --log-level warn",
42
+ "appium:setup": "pnpm run appium:setup:android && pnpm run appium:setup:ios",
43
+ "appium:setup:android": "appium driver list --installed 2>&1 | grep -q uiautomator2 || appium driver install uiautomator2",
44
+ "appium:setup:ios": "appium driver list --installed 2>&1 | grep -q xcuitest || appium driver install xcuitest",
45
+ "e2e:build:android": "cd example && expo run:android --variant release --no-install --no-bundler",
46
+ "e2e:build:ios": "cd example && expo run:ios --configuration Release --no-install --no-bundler",
47
+ "e2e:android": "export PATH=$PATH:$ANDROID_HOME/platform-tools && start-server-and-test appium:start http-get://127.0.0.1:4723/status \"pnpm --filter @algorandfoundation/react-native-passkey-autofill-e2e run test:android\"",
48
+ "e2e:ios": "start-server-and-test appium:start http-get://127.0.0.1:4723/status \"pnpm --filter @algorandfoundation/react-native-passkey-autofill-e2e run test:ios\""
30
49
  },
31
- "author": "Algorand Foundation <michael.feher@algorand.foundation> (https://github.com/algorandfoundation)",
32
- "license": "Apache-2.0",
33
- "homepage": "https://github.com/algorandfoundation/react-native-passkey-autofill#readme",
34
50
  "dependencies": {
35
- "@algorandfoundation/react-native-keystore": "^1.0.0-canary.6"
51
+ "@algorandfoundation/react-native-keystore": "1.0.0-canary.12"
36
52
  },
37
53
  "devDependencies": {
38
- "@expo/config-plugins": "^9.0.0",
39
- "@types/react": "~19.1.1",
40
- "expo": "^55.0.1",
54
+ "@expo/config-plugins": "~55.0.10",
55
+ "@expo/npm-proofread": "^1.0.1",
56
+ "@semantic-release/changelog": "^6.0.3",
57
+ "@semantic-release/exec": "^7.1.0",
58
+ "@semantic-release/git": "^10.0.1",
59
+ "@semantic-release/github": "^12.0.8",
60
+ "@semantic-release/npm": "^13.1.5",
61
+ "@types/jest": "^29.5.14",
62
+ "@types/node": "^22.19.20",
63
+ "@types/react": "~19.2.16",
64
+ "appium": "^2.19.0",
65
+ "appium-uiautomator2-driver": "^3.10.0",
66
+ "appium-xcuitest-driver": "^9.10.5",
67
+ "eslint": "^9.39.4",
68
+ "expo": "55.0.17",
69
+ "expo-doctor": "^1.19.8",
41
70
  "expo-module-scripts": "^55.0.2",
42
- "react-native": "0.82.1"
71
+ "jest": "^29.7.0",
72
+ "lefthook": "2.1.4",
73
+ "oxfmt": "^0.43.0",
74
+ "oxlint": "^1.68.0",
75
+ "prettier": "^3.8.3",
76
+ "react": "19.2.0",
77
+ "react-native": "0.83.6",
78
+ "semantic-release": "^25.0.3",
79
+ "start-server-and-test": "^2.1.5",
80
+ "ts-jest": "^29.4.11",
81
+ "ts-node": "^10.9.2",
82
+ "typescript": "~5.9.3",
83
+ "webdriverio": "^9.27.2"
43
84
  },
44
85
  "peerDependencies": {
45
86
  "expo": "*",
46
87
  "react": "*",
47
88
  "react-native": "*"
89
+ },
90
+ "jest": {
91
+ "preset": "ts-jest",
92
+ "testEnvironment": "node",
93
+ "testMatch": [
94
+ "<rootDir>/src/**/__tests__/**/*.test.ts"
95
+ ]
96
+ },
97
+ "engines": {
98
+ "node": ">=22"
99
+ },
100
+ "packageManager": "pnpm@10.33.0",
101
+ "pnpm": {
102
+ "overrides": {
103
+ "@appium/support@<=7.0.5": ">=7.0.6",
104
+ "@tootallnate/once@<3.0.1": ">=3.0.1",
105
+ "@xmldom/xmldom": "0.8.13",
106
+ "eslint": "^9.7.0",
107
+ "prettier": "^3.0.0",
108
+ "react": "19.2.0",
109
+ "react-dom": "19.2.0",
110
+ "react-test-renderer": "19.2.0",
111
+ "@types/react": "~19.2.10",
112
+ "react-native-mmkv": "4.3.1",
113
+ "react-native-nitro-modules": "0.35.5",
114
+ "react-native-quick-crypto": "1.0.19",
115
+ "expo": "55.0.17",
116
+ "ajv@>=7.0.0-alpha.0 <8.18.0": ">=8.18.0",
117
+ "axios@>=1.0.0 <1.16.0": ">=1.16.0",
118
+ "basic-ftp@<=5.2.2": ">=5.3.0",
119
+ "brace-expansion@>=5.0.0 <5.0.6": ">=5.0.6",
120
+ "diff@>=6.0.0 <8.0.3": ">=8.0.3",
121
+ "fast-uri@<=3.1.1": ">=3.1.2",
122
+ "fast-xml-parser@<5.7.0": ">=5.7.0",
123
+ "form-data@>=4.0.0 <4.0.4": ">=4.0.4",
124
+ "glob@>=10.2.0 <10.5.0": ">=10.5.0",
125
+ "lodash@<=4.17.23": ">=4.18.0",
126
+ "on-headers@<1.1.0": ">=1.1.0",
127
+ "path-to-regexp@<0.1.13": "0.1.13",
128
+ "path-to-regexp@>=8.0.0 <8.4.0": ">=8.4.0",
129
+ "postcss@<8.5.10": ">=8.5.10",
130
+ "qs@<6.15.2": ">=6.15.2",
131
+ "shell-quote@>=1.1.0 <=1.8.3": ">=1.8.4",
132
+ "uuid@<14.0.0": ">=14.0.0",
133
+ "ws@>=8.0.0 <8.20.1": ">=8.20.1",
134
+ "yaml@>=2.0.0 <2.8.3": ">=2.8.3",
135
+ "yauzl@=3.2.0": ">=3.2.1",
136
+ "@babel/plugin-transform-modules-systemjs@>=7.12.0 <=7.29.3": ">=7.29.4"
137
+ }
48
138
  }
49
139
  }
@@ -0,0 +1,36 @@
1
+ packages:
2
+ - "."
3
+ - "example"
4
+ - "e2e"
5
+ minimumReleaseAgeExclude:
6
+ - uuid
7
+ - webpack
8
+ - react
9
+ - "react-dom"
10
+ - "@xmldom/xmldom"
11
+ - "@expo/dom-webview"
12
+ - "@types/node"
13
+ - "@algorandfoundation/wallet-provider"
14
+ - "@algorandfoundation/react-native-keystore"
15
+ - "@algorandfoundation/keystore"
16
+ - "@typescript-eslint/types"
17
+ - "@typescript-eslint/visitor-keys"
18
+ - "react-native-nitro-modules"
19
+ - "react-native-mmkv"
20
+ - "expo"
21
+ - "react-native"
22
+ - "babel-preset-expo"
23
+ - "@expo/config-plugins"
24
+ - "@expo/log-box"
25
+ - "@expo/vector-icons"
26
+ - "expo-asset"
27
+ - "expo-constants"
28
+ - "expo-file-system"
29
+ - "expo-font"
30
+ - "expo-modules-core"
31
+ - "jest-expo"
32
+ - "expo-modules-autolinking"
33
+ - "@expo/cli"
34
+ - "@expo/config"
35
+ - "@expo/prebuild-config"
36
+ - "@expo/metro-config"
@@ -1,3 +1,41 @@
1
1
  export type ReactNativePasskeyAutofillModuleEvents = {
2
+ onPasskeyAdded: (event: { success: boolean }) => void;
3
+ onPasskeyAuthenticated: (event: { success: boolean; credentialId?: string }) => void;
4
+ };
5
+
6
+ export type PasskeyAutofillCredentialIdentity = {
7
+ credentialId: string;
8
+ relyingPartyIdentifier?: string;
9
+ rpId?: string;
10
+ origin?: string;
11
+ userName?: string;
12
+ name?: string;
13
+ userHandle: string;
14
+ userId?: string;
15
+ privateKey?: string;
16
+ privateKeyBase64?: string;
17
+ publicKey?: string;
18
+ publicKeyBase64?: string;
19
+ createdAt?: number;
20
+ lastUsedAt?: number;
21
+ parentKeyId?: string;
22
+ };
23
+
24
+ /**
25
+ * Capabilities advertised by this credential provider. Exposed so the
26
+ * surrounding wallet UI / RP-side wallet can know which WebAuthn extensions
27
+ * are wired up natively before issuing a passkey request.
28
+ */
29
+ export type PasskeyAutofillCapabilities = {
30
+ /**
31
+ * Whether the WebAuthn `prf` extension (a.k.a. `hmac-secret`) is supported
32
+ * by this credential provider. PRF outputs are derived deterministically
33
+ * from the wallet HD root secret so that restoring the wallet seed
34
+ * reproduces the same secrets on another device.
35
+ */
36
+ prf: boolean;
37
+ };
2
38
 
39
+ export const PASSKEY_AUTOFILL_CAPABILITIES: PasskeyAutofillCapabilities = {
40
+ prf: true,
3
41
  };
@@ -1,6 +1,10 @@
1
- import { NativeModule, requireNativeModule } from 'expo';
1
+ import { NativeModule, requireNativeModule } from "expo";
2
+ import { Platform } from "react-native";
2
3
 
3
- import { ReactNativePasskeyAutofillModuleEvents } from './ReactNativePasskeyAutofill.types';
4
+ import {
5
+ PasskeyAutofillCredentialIdentity,
6
+ ReactNativePasskeyAutofillModuleEvents,
7
+ } from "./ReactNativePasskeyAutofill.types";
4
8
 
5
9
  declare class ReactNativePasskeyAutofillModule extends NativeModule<ReactNativePasskeyAutofillModuleEvents> {
6
10
  setMasterKey(secret: string): Promise<void>;
@@ -8,7 +12,66 @@ declare class ReactNativePasskeyAutofillModule extends NativeModule<ReactNativeP
8
12
  getHdRootKeyId(): Promise<string | null>;
9
13
  configureIntentActions(getPasskeyAction: string, createPasskeyAction: string): Promise<void>;
10
14
  clearCredentials(): Promise<void>;
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
+ */
22
+ getStoredCredentials(): Promise<PasskeyAutofillCredentialIdentity[]>;
23
+ /**
24
+ * iOS: returns diagnostic strings from the AutoFill extension.
25
+ * Android: no-op returning `[]`.
26
+ */
27
+ getDiagnostics(): Promise<string[]>;
28
+ /**
29
+ * Replaces the iOS AutoFill passkey identity store with credentials
30
+ * available to the native Credential Provider extension. Each item must
31
+ * include a credential id, relying party (`relyingPartyIdentifier`,
32
+ * `rpId`, or `origin`), user handle, and P-256 private key material.
33
+ * Android ignores this method (no-op) because the provider reads MMKV
34
+ * directly.
35
+ */
36
+ replaceCredentialIdentities(credentials: PasskeyAutofillCredentialIdentity[]): Promise<void>;
37
+ refreshCredentialIdentities(): Promise<void>;
38
+ /**
39
+ * Resolves to `true` when this app is registered as the active
40
+ * credential/autofill provider on the current device (Android 14+
41
+ * credential provider or iOS AutoFill). Useful for gating passkey UI
42
+ * and for E2E tests that need to confirm the prompt that appears
43
+ * belongs to this provider and not a third party.
44
+ */
45
+ isProviderActive(): Promise<boolean>;
46
+ /**
47
+ * Opens the OS credential/autofill provider settings screen so the
48
+ * user can enable this app as the active provider. Resolves to `true`
49
+ * if a settings screen could be launched, `false` otherwise.
50
+ */
51
+ openProviderSettings(): Promise<boolean>;
11
52
  }
12
53
 
13
54
  // This call loads the native module object from the JSI.
14
- export default requireNativeModule<ReactNativePasskeyAutofillModule>('ReactNativePasskeyAutofill');
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;
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  // Reexport the native module. On web, it will be resolved to ReactNativePasskeyAutofillModule.web.ts
2
2
  // and on native platforms to ReactNativePasskeyAutofillModule.ts
3
- export { default } from './ReactNativePasskeyAutofillModule';
4
- export * from './ReactNativePasskeyAutofill.types';
3
+ export { default } from "./ReactNativePasskeyAutofillModule";
4
+ export * from "./ReactNativePasskeyAutofill.types";
package/tsconfig.json CHANGED
@@ -1,8 +1,8 @@
1
- // @generated by expo-module-scripts
2
1
  {
3
2
  "extends": "expo-module-scripts/tsconfig.base",
4
3
  "compilerOptions": {
5
- "outDir": "./build"
4
+ "outDir": "./build",
5
+ "rootDir": "./src"
6
6
  },
7
7
  "include": ["./src"],
8
8
  "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__rsc_tests__/*"]
package/.eslintrc.js DELETED
@@ -1,5 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- extends: ['universe/native', 'universe/web'],
4
- ignorePatterns: ['build'],
5
- };