@algorandfoundation/react-native-passkey-autofill 1.0.0-canary.2 → 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/.nvmrc +1 -0
- package/.oxfmtrc.json +4 -0
- package/.oxlintrc.json +14 -0
- package/.releaserc +30 -0
- package/ARCHITECTURE.md +8 -0
- package/CHANGELOG.md +108 -0
- package/CONTRIBUTING.md +15 -5
- package/README.md +150 -11
- package/android/build.gradle +33 -10
- package/android/libs/repo/co/algorand/dP256Android/1.0.0/dP256Android-1.0.0.pom +9 -0
- package/android/src/main/AndroidManifest.xml +9 -3
- package/android/src/main/java/co/algorand/passkeyautofill/CreatePasskeyActivity.kt +396 -44
- package/android/src/main/java/co/algorand/passkeyautofill/GetPasskeyActivity.kt +583 -44
- package/android/src/main/java/co/algorand/passkeyautofill/Prf.kt +184 -0
- package/android/src/main/java/co/algorand/passkeyautofill/ReactNativePasskeyAutofillModule.kt +190 -2
- package/android/src/main/java/co/algorand/passkeyautofill/WebAuthn.kt +135 -0
- package/android/src/main/java/co/algorand/passkeyautofill/auth/BiometricRequirement.kt +86 -0
- package/android/src/main/java/co/algorand/passkeyautofill/credentials/Credential.kt +2 -1
- package/android/src/main/java/co/algorand/passkeyautofill/credentials/CredentialRepository.kt +314 -53
- package/android/src/main/java/co/algorand/passkeyautofill/service/PasskeyAutofillCredentialProviderService.kt +152 -38
- package/android/src/main/java/co/algorand/passkeyautofill/utils/PasskeyUtils.kt +119 -0
- package/android/src/main/res/values/styles.xml +10 -0
- package/app.plugin.js +590 -60
- package/assets/banner.png +0 -0
- package/build/ReactNativePasskeyAutofill.types.d.ts +41 -1
- package/build/ReactNativePasskeyAutofill.types.d.ts.map +1 -1
- package/build/ReactNativePasskeyAutofill.types.js +3 -1
- package/build/ReactNativePasskeyAutofill.types.js.map +1 -1
- package/build/ReactNativePasskeyAutofillModule.d.ts +41 -4
- package/build/ReactNativePasskeyAutofillModule.d.ts.map +1 -1
- package/build/ReactNativePasskeyAutofillModule.js +21 -2
- package/build/ReactNativePasskeyAutofillModule.js.map +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -2
- package/build/index.js.map +1 -1
- package/e2e/README.md +155 -0
- package/e2e/driver/adb.ts +73 -0
- package/e2e/driver/capabilities.ts +106 -0
- package/e2e/driver/credentialProvider.ts +104 -0
- package/e2e/driver/fingerprint.ts +885 -0
- package/e2e/driver/selectors.ts +103 -0
- package/e2e/driver/session.ts +25 -0
- package/e2e/jest.config.ts +20 -0
- package/e2e/package.json +11 -0
- package/e2e/tests/passkey.test.ts +190 -0
- package/e2e/tsconfig.json +16 -0
- package/ios/AutofillCredentialProvider/BiometricRequirement.swift +34 -0
- package/ios/AutofillCredentialProvider/CredentialProviderViewController.swift +568 -0
- package/ios/AutofillCredentialProvider/PasskeyAutofillCredentialProvider-Bridging-Header.h +1 -0
- package/ios/AutofillCredentialProvider/PasskeyCredentialStore.swift +612 -0
- package/ios/AutofillCredentialProvider/PasskeyKeystoreMMKV.h +25 -0
- package/ios/AutofillCredentialProvider/PasskeyKeystoreMMKV.mm +86 -0
- package/ios/AutofillCredentialProvider/Prf.swift +89 -0
- package/ios/AutofillCredentialProvider/WebAuthn.swift +186 -0
- package/ios/ReactNativePasskeyAutofill.podspec +11 -2
- package/ios/ReactNativePasskeyAutofillModule.swift +170 -15
- package/lefthook.yml +14 -0
- package/package.json +115 -25
- package/pnpm-workspace.yaml +36 -0
- package/src/ReactNativePasskeyAutofill.types.ts +38 -0
- package/src/ReactNativePasskeyAutofillModule.ts +66 -3
- package/src/index.ts +2 -2
- package/tsconfig.json +2 -2
- package/.eslintrc.js +0 -5
- /package/android/libs/{dP256Android-release.aar → repo/co/algorand/dP256Android/1.0.0/dP256Android-1.0.0.aar} +0 -0
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22
|
package/.oxfmtrc.json
ADDED
package/.oxlintrc.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": ["eslint", "react", "react-perf", "typescript", "unicorn", "oxc"],
|
|
4
|
+
"categories": {
|
|
5
|
+
"correctness": "error"
|
|
6
|
+
},
|
|
7
|
+
"rules": {
|
|
8
|
+
"react-hooks/exhaustive-deps": "warn",
|
|
9
|
+
"eslint/no-unused-vars": "warn"
|
|
10
|
+
},
|
|
11
|
+
"env": {
|
|
12
|
+
"builtin": true
|
|
13
|
+
}
|
|
14
|
+
}
|
package/.releaserc
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": [
|
|
3
|
+
"release",
|
|
4
|
+
{
|
|
5
|
+
"name": "main",
|
|
6
|
+
"prerelease": "canary",
|
|
7
|
+
"channel": "next"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"plugins": [
|
|
11
|
+
"@semantic-release/commit-analyzer",
|
|
12
|
+
"@semantic-release/release-notes-generator",
|
|
13
|
+
"@semantic-release/changelog",
|
|
14
|
+
[
|
|
15
|
+
"@semantic-release/exec",
|
|
16
|
+
{
|
|
17
|
+
"prepareCmd": "pnpm fmt"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"@semantic-release/npm",
|
|
21
|
+
[
|
|
22
|
+
"@semantic-release/git",
|
|
23
|
+
{
|
|
24
|
+
"assets": ["package.json", "pnpm-lock.yaml", "CHANGELOG.md"],
|
|
25
|
+
"message": "chore(release): ${nextRelease.version} \n\n${nextRelease.notes}"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"@semantic-release/github"
|
|
29
|
+
]
|
|
30
|
+
}
|
package/ARCHITECTURE.md
CHANGED
|
@@ -35,9 +35,11 @@ The Android part is implemented using Kotlin and follows the Android Autofill Fr
|
|
|
35
35
|
The `PasskeyAutofillCredentialProviderService` is the core of the Android implementation. It extends the Android Jetpack `CredentialProviderService` to provide passkeys directly to the system's Credential Manager.
|
|
36
36
|
|
|
37
37
|
##### What is `CredentialProviderService`?
|
|
38
|
+
|
|
38
39
|
Introduced in Android 14 (API level 34) and backported to Android 9 via the Jetpack Credentials library, this service allows password managers and other credential providers to integrate with the system's unified sign-in flow. When a user interacts with a sign-in or sign-up field, the system queries registered services to provide available credentials.
|
|
39
40
|
|
|
40
41
|
##### Handling the Chain of Trust
|
|
42
|
+
|
|
41
43
|
The "Chain of Trust" ensures that passkeys are only used by the legitimate owners of a domain or application. This is handled through several layers of validation:
|
|
42
44
|
|
|
43
45
|
1. **Digital Asset Links**: For a passkey to be used across a website and an Android app, the website must host a `/.well-known/assetlinks.json` file that explicitly authorizes the Android app (via its package name and certificate fingerprint). This establishes a cryptographically verified link between the web origin and the mobile application.
|
|
@@ -61,7 +63,13 @@ The following methods are exposed to the JavaScript layer:
|
|
|
61
63
|
- `getHdRootKeyId()`: Retrieves the current HD root key ID.
|
|
62
64
|
- `configureIntentActions(getPasskeyAction: string, createPasskeyAction: string)`: Configures the intent actions used for Passkey flows.
|
|
63
65
|
- `clearCredentials()`: Clears all stored credentials.
|
|
66
|
+
- `isProviderActive()`: Returns `true` if this app is the user-selected system credential/autofill provider. Uses Android's `Settings.Secure("credential_service"[_primary])` (API 34+) and iOS's `ASCredentialIdentityStore.getState`. Useful both for gating passkey UI at runtime and for E2E tests, which need to confirm that an OS passkey prompt is served by _this_ provider rather than any other installed one.
|
|
67
|
+
- `openProviderSettings()`: Deep-links the user to the OS credential/autofill provider settings so they can enable this app as the active provider. Resolves to `true` if a settings screen could be launched.
|
|
64
68
|
|
|
65
69
|
## Security Considerations
|
|
66
70
|
|
|
67
71
|
As this module handles sensitive information (Passkeys), it is crucial to ensure that keys and secrets are handled securely in the native layers. Master keys should be stored in secure storage (like Android Keystore or iOS Keychain).
|
|
72
|
+
|
|
73
|
+
## End-to-End Tests
|
|
74
|
+
|
|
75
|
+
The `e2e/` workspace drives the `example/` app with Appium 2 + WebdriverIO, executed through Jest. The Android job uses the UiAutomator2 driver; the iOS job uses XCUITest. The happy-path spec mirrors the example and exercises passkey registration and assertion against `https://debug.liquidauth.com`. See [`e2e/README.md`](./e2e/README.md) for local usage and the [`E2E` workflow](./.github/workflows/e2e.yml) for CI.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
|
|
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)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **android:** credential repository filter types in getAllCredentials ([2b6523f](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/2b6523fc25aaadb5152b0b0d3d6b3a56e9de1759))
|
|
12
|
+
|
|
13
|
+
# [1.0.0-canary.18](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.17...v1.0.0-canary.18) (2026-06-09)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- PRF extension support for deterministically derived secrets ([4b5b05a](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/4b5b05ab146e39b191855ccb01fd4aca5ed32819))
|
|
18
|
+
|
|
19
|
+
# [1.0.0-canary.17](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.16...v1.0.0-canary.17) (2026-05-28)
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
- allow configurable AAGUID ([d5a8176](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/d5a81765ba150e9873e18875f0ac9f7bc4aede93))
|
|
24
|
+
- **Android:** add NoOp methods for iOS-specific functionality ([e2c64db](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/e2c64db479cda70c7c61b7ab3021b70db949a15b))
|
|
25
|
+
- compile issue ([16b939c](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/16b939cb555e75bd052ce57a41a838810a2770cd))
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
- configurable credential requirement ([dc09916](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/dc09916d1ee5c8c9d33e53b57f00171472f4f665))
|
|
30
|
+
- record lastUsedAt/count and fix error handling ([47b40ab](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/47b40abf692c0f7cffee6f2266c66f61f8006a65))
|
|
31
|
+
|
|
32
|
+
# [1.0.0-canary.16](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.15...v1.0.0-canary.16) (2026-05-15)
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
- align ios autofill root key flow ([1287f57](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/1287f57952b41829032ef368bad826624375e9a3))
|
|
37
|
+
|
|
38
|
+
### Features
|
|
39
|
+
|
|
40
|
+
- add iOS passkey autofill provider ([9aad8f8](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/9aad8f8ceb2ad54cf53847b60dd6e70d5b2a1f2b))
|
|
41
|
+
|
|
42
|
+
# [1.0.0-canary.15](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.14...v1.0.0-canary.15) (2026-04-10)
|
|
43
|
+
|
|
44
|
+
### Bug Fixes
|
|
45
|
+
|
|
46
|
+
- use bouncycastle bcprov-jdk15to18 for android ([cd860b2](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/cd860b2012f2189e854137afa55c7a0c0dc1b94c))
|
|
47
|
+
|
|
48
|
+
# [1.0.0-canary.14](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.13...v1.0.0-canary.14) (2026-04-10)
|
|
49
|
+
|
|
50
|
+
### Bug Fixes
|
|
51
|
+
|
|
52
|
+
- switch to maven for local AAR dependency ([f4e52b4](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/f4e52b46d618e6e8a7972e649c5d75d768e32339))
|
|
53
|
+
|
|
54
|
+
# [1.0.0-canary.13](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.12...v1.0.0-canary.13) (2026-04-10)
|
|
55
|
+
|
|
56
|
+
### Bug Fixes
|
|
57
|
+
|
|
58
|
+
- bump keystore to 1.0.0-canary.9 ([f093bab](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/f093bab0b74d5cbf93e8f03ecd8d56c77597d3f5))
|
|
59
|
+
|
|
60
|
+
# [1.0.0-canary.12](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.11...v1.0.0-canary.12) (2026-04-10)
|
|
61
|
+
|
|
62
|
+
### Features
|
|
63
|
+
|
|
64
|
+
- **android:** biometrics single tap and fallback ([a6cbe4c](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/a6cbe4cf51579b15b2a219e1e06e843bb6001ed4))
|
|
65
|
+
- **android:** support DER format ([492034f](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/492034f8b031e795b2575be69da8c3a1adb9096c))
|
|
66
|
+
|
|
67
|
+
# [1.0.0-canary.11](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.10...v1.0.0-canary.11) (2026-03-31)
|
|
68
|
+
|
|
69
|
+
### Bug Fixes
|
|
70
|
+
|
|
71
|
+
- update service url ([ffc2c22](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/ffc2c22e18661cf5a159eef5b06303b94981df6b))
|
|
72
|
+
|
|
73
|
+
# [1.0.0-canary.10](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.9...v1.0.0-canary.10) (2026-03-30)
|
|
74
|
+
|
|
75
|
+
### Bug Fixes
|
|
76
|
+
|
|
77
|
+
- bouncy castle provider insertion order ([9e9039d](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/9e9039deab02f75f5a4397cedc098c0ca2e24875))
|
|
78
|
+
|
|
79
|
+
# [1.0.0-canary.9](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.8...v1.0.0-canary.9) (2026-03-30)
|
|
80
|
+
|
|
81
|
+
### Bug Fixes
|
|
82
|
+
|
|
83
|
+
- x logins, refactor example views to be dialogs ([e7a1ccf](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/e7a1ccf91f88b5c369ff3082f06d538fc8b3db89))
|
|
84
|
+
|
|
85
|
+
# [1.0.0-canary.8](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.7...v1.0.0-canary.8) (2026-03-27)
|
|
86
|
+
|
|
87
|
+
### Features
|
|
88
|
+
|
|
89
|
+
- passkey added and authenticated events ([b9871f4](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/b9871f475a9a633ae98d30a702ef2814ec8ae5e0))
|
|
90
|
+
|
|
91
|
+
# [1.0.0-canary.7](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.6...v1.0.0-canary.7) (2026-03-24)
|
|
92
|
+
|
|
93
|
+
### Bug Fixes
|
|
94
|
+
|
|
95
|
+
- update package lock and include in CI/CD ([1d775cd](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/1d775cdde170a6ef400a1ee8a01fce759ddb1f1e))
|
|
96
|
+
|
|
97
|
+
# [1.0.0-canary.6](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.5...v1.0.0-canary.6) (2026-03-24)
|
|
98
|
+
|
|
99
|
+
### Bug Fixes
|
|
100
|
+
|
|
101
|
+
- include banner for package README ([60a6559](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/60a655932faac6de57daf0f30261f2d03a2c1476))
|
|
102
|
+
|
|
103
|
+
# [1.0.0-canary.5](https://github.com/algorandfoundation/react-native-passkey-autofill/compare/v1.0.0-canary.4...v1.0.0-canary.5) (2026-03-24)
|
|
104
|
+
|
|
105
|
+
### Features
|
|
106
|
+
|
|
107
|
+
- **android:** add allowed credentials mapping ([e7134fc](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/e7134fc1e5bf09649155e38010e4ef9728580812))
|
|
108
|
+
- **android:** credential compatibility with system-provided clientDataHash and compact JSON ([428e96f](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/428e96f89b73cd35fdbfaa6669f39da68403e17e))
|
package/CONTRIBUTING.md
CHANGED
|
@@ -10,11 +10,11 @@ To get started with the project, follow these steps:
|
|
|
10
10
|
2. **Clone the repository** to your local machine.
|
|
11
11
|
3. **Install dependencies** at the root of the project:
|
|
12
12
|
```bash
|
|
13
|
-
|
|
13
|
+
pnpm install
|
|
14
14
|
```
|
|
15
15
|
4. **Build the project**:
|
|
16
16
|
```bash
|
|
17
|
-
|
|
17
|
+
pnpm run build
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Development Workflow
|
|
@@ -29,7 +29,7 @@ The project includes an example app in the `example/` directory. You can use it
|
|
|
29
29
|
```
|
|
30
30
|
2. Install example dependencies:
|
|
31
31
|
```bash
|
|
32
|
-
|
|
32
|
+
pnpm install
|
|
33
33
|
```
|
|
34
34
|
3. Prebuild the example for Android or iOS:
|
|
35
35
|
```bash
|
|
@@ -37,9 +37,9 @@ The project includes an example app in the `example/` directory. You can use it
|
|
|
37
37
|
```
|
|
38
38
|
4. Run the example app:
|
|
39
39
|
```bash
|
|
40
|
-
|
|
40
|
+
pnpm run android # for Android
|
|
41
41
|
# or
|
|
42
|
-
|
|
42
|
+
pnpm run ios # for iOS
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
### Code Style
|
|
@@ -50,6 +50,16 @@ The project includes an example app in the `example/` directory. You can use it
|
|
|
50
50
|
|
|
51
51
|
For more details on how the project is structured, see the [Architecture Guide](./ARCHITECTURE.md).
|
|
52
52
|
|
|
53
|
+
### Testing
|
|
54
|
+
|
|
55
|
+
Before submitting a Pull Request, ensure that both JavaScript and Native tests pass:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm run test:all
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For more details on the testing strategy, refer to the [Testing section in README.md](./README.md#🧪-testing).
|
|
62
|
+
|
|
53
63
|
## Submitting a Pull Request
|
|
54
64
|
|
|
55
65
|
1. Create a new branch for your feature or bugfix:
|
package/README.md
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @algorandfoundation/react-native-passkey-autofill
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/algorandfoundation/react-native-passkey-autofill/refs/heads/main/assets/banner.png" width="100%" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Passkey AutoFill for React Native using DP256.
|
|
9
|
+
</p>
|
|
4
10
|
|
|
5
11
|
# 🚀 Get Started
|
|
6
12
|
|
|
7
13
|
For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
|
|
8
14
|
|
|
9
|
-
### Add the package to your
|
|
15
|
+
### Add the package to your dependencies
|
|
10
16
|
|
|
11
17
|
```bash
|
|
12
|
-
|
|
18
|
+
pnpm add @algorandfoundation/react-native-passkey-autofill
|
|
13
19
|
```
|
|
14
20
|
|
|
15
21
|
### Configure for Android
|
|
@@ -25,7 +31,7 @@ If you are using Expo, you can configure the plugin in your `app.json` or `app.c
|
|
|
25
31
|
"expo": {
|
|
26
32
|
"plugins": [
|
|
27
33
|
[
|
|
28
|
-
"react-native-passkey-autofill",
|
|
34
|
+
"@algorandfoundation/react-native-passkey-autofill",
|
|
29
35
|
{
|
|
30
36
|
"site": "https://your-fido-server.com",
|
|
31
37
|
"label": "My Custom Credential Provider"
|
|
@@ -36,13 +42,78 @@ If you are using Expo, you can configure the plugin in your `app.json` or `app.c
|
|
|
36
42
|
}
|
|
37
43
|
```
|
|
38
44
|
|
|
39
|
-
- `site`: The URL of your FIDO server (default: `https://
|
|
45
|
+
- `site`: The URL of your FIDO server (default: `https://debug.liquidauth.com`).
|
|
40
46
|
- `label`: The name of the credential provider as it appears in Android settings (default: `My Credential Provider`).
|
|
47
|
+
- `aaguid`: Optional authenticator AAGUID (UUID string) embedded in attestation responses to identify your authenticator to relying parties. When omitted, iOS uses the module's built-in default and Android uses the all-zero AAGUID emitted by the platform. Set the same value across all your apps (iOS, Android, web) so they present one identity.
|
|
48
|
+
- `biometricRequirement`: Controls which authenticators satisfy passkey user verification. See [Biometric Requirement](#biometric-requirement) below.
|
|
49
|
+
|
|
50
|
+
#### Biometric Requirement
|
|
51
|
+
|
|
52
|
+
`biometricRequirement` is an optional string property that controls which authenticators are accepted for user verification during passkey creation and authentication.
|
|
53
|
+
|
|
54
|
+
| Value | Android | iOS |
|
|
55
|
+
| ------------------------------ | ---------------------------------------------------------------------------------------- | ----------------------------------- |
|
|
56
|
+
| `strong` | Strong biometric only | Biometrics only (passcode rejected) |
|
|
57
|
+
| `strongOrCredential` (default) | Strong biometric or device PIN/pattern/password | Biometrics or device passcode |
|
|
58
|
+
| `weakOrCredential` | Weak biometric or device credential (key is **not** crypto-bound — a security trade-off) | Biometrics or device passcode |
|
|
59
|
+
|
|
60
|
+
**Notes:**
|
|
61
|
+
|
|
62
|
+
- Android cannot gate a hardware-backed key on weak biometrics, so `weakOrCredential` stores the key without user-authentication binding and uses the biometric prompt as a UI gate only. Use it only when you accept that trade-off.
|
|
63
|
+
- The default `strongOrCredential` is more permissive than the previous strong-only behavior; integrators upgrading will additionally allow the device credential on Android (iOS behavior is unchanged).
|
|
64
|
+
|
|
65
|
+
### Configure for iOS
|
|
66
|
+
|
|
67
|
+
iOS passkey AutoFill requires a Credential Provider extension, an App Group shared between the app and extension, and an associated domain for Web Credentials. The Expo config plugin can create and wire the extension during prebuild:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"expo": {
|
|
72
|
+
"ios": {
|
|
73
|
+
"bundleIdentifier": "com.example.wallet",
|
|
74
|
+
"associatedDomains": ["webcredentials:your-fido-server.com"],
|
|
75
|
+
"entitlements": {
|
|
76
|
+
"com.apple.developer.authentication-services.autofill-credential-provider": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"plugins": [
|
|
80
|
+
[
|
|
81
|
+
"@algorandfoundation/react-native-passkey-autofill",
|
|
82
|
+
{
|
|
83
|
+
"site": "https://your-fido-server.com",
|
|
84
|
+
"label": "My Custom Credential Provider",
|
|
85
|
+
"appGroup": "group.com.example.wallet.passkey-autofill",
|
|
86
|
+
"appleTeamId": "YOUR_TEAM_ID"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
For iOS integration, make sure that:
|
|
95
|
+
|
|
96
|
+
- The app and Credential Provider extension both have the AutoFill Credential Provider capability.
|
|
97
|
+
- The app and extension both have the same App Group entitlement.
|
|
98
|
+
- The app has a `webcredentials:<domain>` associated domain, and that domain serves a valid `apple-app-site-association` file for the app identifier.
|
|
99
|
+
- The deployment target is iOS 17 or newer for passkey credential provider support.
|
|
100
|
+
- The generated extension target can link `AuthenticationServices.framework`, `CryptoKit.framework`, `MMKVCore`, and the deterministic P-256 Swift package.
|
|
101
|
+
- `NSFaceIDUsageDescription` is present when biometric authentication is used.
|
|
102
|
+
|
|
103
|
+
At runtime, the app must provide the native side with the master key, identify the HD root key stored in MMKV, and keep the iOS identity store in sync:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
await ReactNativePasskeyAutofill.setMasterKey(masterKeyHex);
|
|
107
|
+
await ReactNativePasskeyAutofill.setHdRootKeyId(hdRootKeyId);
|
|
108
|
+
await ReactNativePasskeyAutofill.refreshCredentialIdentities();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Call `refreshCredentialIdentities()` after creating, importing, deleting, or restoring passkeys so iOS AutoFill sees the current credentials.
|
|
41
112
|
|
|
42
113
|
## Usage
|
|
43
114
|
|
|
44
115
|
```typescript
|
|
45
|
-
import ReactNativePasskeyAutofill from
|
|
116
|
+
import ReactNativePasskeyAutofill from "@algorandfoundation/react-native-passkey-autofill";
|
|
46
117
|
|
|
47
118
|
// 1. Set the master key for encryption (hex string)
|
|
48
119
|
await ReactNativePasskeyAutofill.setMasterKey(masterKeyHex);
|
|
@@ -52,25 +123,93 @@ await ReactNativePasskeyAutofill.setHdRootKeyId(hdRootKeyId);
|
|
|
52
123
|
|
|
53
124
|
// 3. Configure intent actions for the Passkey flows
|
|
54
125
|
await ReactNativePasskeyAutofill.configureIntentActions(
|
|
55
|
-
|
|
56
|
-
|
|
126
|
+
"your.package.name.GET_PASSKEY",
|
|
127
|
+
"your.package.name.CREATE_PASSKEY",
|
|
57
128
|
);
|
|
58
129
|
|
|
59
130
|
// Optional: Clear credentials
|
|
60
131
|
await ReactNativePasskeyAutofill.clearCredentials();
|
|
61
132
|
```
|
|
62
133
|
|
|
134
|
+
## Events
|
|
135
|
+
|
|
136
|
+
You can listen for events emitted by the native module when a passkey is successfully added or authenticated.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import ReactNativePasskeyAutofill from "@algorandfoundation/react-native-passkey-autofill";
|
|
140
|
+
import { useEffect } from "react";
|
|
141
|
+
|
|
142
|
+
// ... inside a component or hook
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const addedSubscription = ReactNativePasskeyAutofill.addListener("onPasskeyAdded", (event) => {
|
|
145
|
+
console.log("Passkey added successfully:", event.success);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const authSubscription = ReactNativePasskeyAutofill.addListener(
|
|
149
|
+
"onPasskeyAuthenticated",
|
|
150
|
+
(event) => {
|
|
151
|
+
console.log("Passkey authenticated successfully:", event.success);
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return () => {
|
|
156
|
+
addedSubscription.remove();
|
|
157
|
+
authSubscription.remove();
|
|
158
|
+
};
|
|
159
|
+
}, []);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## 🧪 Testing
|
|
163
|
+
|
|
164
|
+
The project is set up with a comprehensive testing approach covering both JavaScript and Native (Kotlin) sides.
|
|
165
|
+
|
|
166
|
+
### JavaScript Tests
|
|
167
|
+
|
|
168
|
+
Run unit tests for the TypeScript module using Jest:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
pnpm test
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Native Android Tests
|
|
175
|
+
|
|
176
|
+
Run unit tests for the Kotlin code using JUnit and Robolectric. These tests are executed via the example app's Gradle wrapper:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
pnpm run test:android
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### All Tests
|
|
183
|
+
|
|
184
|
+
Run both JS and Native tests:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
pnpm run test:all
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Continuous Integration (CI)
|
|
191
|
+
|
|
192
|
+
The project includes a GitHub Actions workflow that automatically runs linting, JS tests, and Native Android tests on every push and pull request to the `main` or `release` branches. You can find the configuration in `.github/workflows/ci.yml`.
|
|
193
|
+
|
|
194
|
+
### Integration Testing (E2E)
|
|
195
|
+
|
|
196
|
+
Full end-to-end tests driving the example app with Appium and WebdriverIO are available in the [`e2e/`](./e2e) directory. These tests exercise the entire passkey creation and usage flow on Android emulators and iOS simulators. See the [E2E README](./e2e/README.md) for more details.
|
|
197
|
+
|
|
198
|
+
## 📱 Example App
|
|
199
|
+
|
|
200
|
+
The [example](./example) app demonstrates how to integrate this module with a full wallet implementation.
|
|
201
|
+
|
|
63
202
|
# 🤝 Contributing
|
|
64
203
|
|
|
65
204
|
Contributions are very welcome! Please refer to guidelines described in the [contributing guide](./CONTRIBUTING.md).
|
|
66
205
|
|
|
67
206
|
# 💖 Acknowledgements
|
|
68
207
|
|
|
69
|
-
This has been the
|
|
208
|
+
This has been the culmination of many different efforts and ideas. We would like to thank the following individuals and organizations for their contributions:
|
|
70
209
|
|
|
71
210
|
- [Bruno Martins](https://github.com/bmartins) the architect at [Algorand Foundation](https://github.com/algorandfoundation) for conceptualizing and guiding the project.
|
|
72
211
|
- [HashMapsData2Value](https://github.com/HashMapsData2Value) for his guidance and support in DP256 and XHD and his work on the native autofill libraries.
|
|
73
|
-
- [Will Beaumont](https://github.com/mjbeau) for working
|
|
212
|
+
- [Will Beaumont](https://github.com/mjbeau) for working through integration within the Pera wallet
|
|
74
213
|
- [Michael T Chuang](https://github.com/michaeltchuang) for his work in KMP integrations and client libraries.
|
|
75
214
|
|
|
76
215
|
# 📄 License
|
package/android/build.gradle
CHANGED
|
@@ -27,6 +27,7 @@ if (useManagedAndroidSdkVersions) {
|
|
|
27
27
|
defaultConfig {
|
|
28
28
|
minSdkVersion safeExtGet("minSdkVersion", 24)
|
|
29
29
|
targetSdkVersion safeExtGet("targetSdkVersion", 36)
|
|
30
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -43,26 +44,48 @@ android {
|
|
|
43
44
|
kotlinOptions {
|
|
44
45
|
freeCompilerArgs += ["-opt-in=kotlin.time.ExperimentalTime", "-opt-in=kotlin.RequiresOptIn"]
|
|
45
46
|
}
|
|
47
|
+
testOptions {
|
|
48
|
+
unitTests.includeAndroidResources = true
|
|
49
|
+
// Let unmocked android.jar calls (e.g. android.util.Log) return defaults instead of throwing
|
|
50
|
+
// "Stub!" in JVM unit tests.
|
|
51
|
+
unitTests.returnDefaultValues = true
|
|
52
|
+
}
|
|
53
|
+
packagingOptions {
|
|
54
|
+
pickFirst '**/libc++_shared.so'
|
|
55
|
+
pickFirst '**/libmmkv.so'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
repositories {
|
|
60
|
+
maven {
|
|
61
|
+
url = uri("file://${projectDir}/libs/repo")
|
|
62
|
+
}
|
|
46
63
|
}
|
|
47
64
|
|
|
48
65
|
dependencies {
|
|
49
|
-
implementation "androidx.
|
|
50
|
-
implementation "androidx.credentials:credentials
|
|
66
|
+
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
|
67
|
+
implementation "androidx.credentials:credentials:1.5.0-rc01"
|
|
68
|
+
implementation "androidx.credentials:credentials-play-services-auth:1.5.0-rc01"
|
|
51
69
|
implementation "androidx.appcompat:appcompat:1.7.0"
|
|
52
70
|
implementation "androidx.activity:activity-ktx:1.8.2"
|
|
53
71
|
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4"
|
|
54
72
|
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.4"
|
|
55
73
|
implementation "io.github.zhongwuzw:mmkv:2.3.0"
|
|
56
|
-
implementation "org.bouncycastle:bcprov-
|
|
74
|
+
implementation "org.bouncycastle:bcprov-jdk15to18:1.78.1"
|
|
57
75
|
implementation "androidx.datastore:datastore-preferences:1.1.1"
|
|
58
|
-
implementation
|
|
76
|
+
implementation "co.algorand:dP256Android:1.0.0"
|
|
59
77
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
60
78
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0"
|
|
61
79
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0"
|
|
62
|
-
}
|
|
63
80
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
// Testing dependencies
|
|
82
|
+
testImplementation "junit:junit:4.13.2"
|
|
83
|
+
testImplementation "org.robolectric:robolectric:4.11.1"
|
|
84
|
+
testImplementation "io.mockk:mockk:1.13.8"
|
|
85
|
+
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0"
|
|
86
|
+
|
|
87
|
+
androidTestImplementation "androidx.test:runner:1.5.2"
|
|
88
|
+
androidTestImplementation "androidx.test:rules:1.5.0"
|
|
89
|
+
androidTestImplementation "androidx.test.ext:junit:1.1.5"
|
|
90
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
|
|
91
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
3
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
4
|
+
<modelVersion>4.0.0</modelVersion>
|
|
5
|
+
<groupId>co.algorand</groupId>
|
|
6
|
+
<artifactId>dP256Android</artifactId>
|
|
7
|
+
<version>1.0.0</version>
|
|
8
|
+
<packaging>aar</packaging>
|
|
9
|
+
</project>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
2
|
xmlns:tools="http://schemas.android.com/tools">
|
|
3
|
+
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
|
3
4
|
<application>
|
|
4
5
|
<service
|
|
5
6
|
android:name=".service.PasskeyAutofillCredentialProviderService"
|
|
6
7
|
android:enabled="true"
|
|
7
8
|
android:exported="true"
|
|
8
9
|
android:label="@string/passkey_autofill_label"
|
|
9
|
-
android:icon="@mipmap/ic_launcher"
|
|
10
10
|
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
|
|
11
11
|
tools:targetApi="upside_down_cake">
|
|
12
12
|
<intent-filter>
|
|
@@ -16,13 +16,19 @@
|
|
|
16
16
|
android:name="android.credentials.provider"
|
|
17
17
|
android:resource="@xml/provider" />
|
|
18
18
|
</service>
|
|
19
|
-
<activity android:name=".GetPasskeyActivity"
|
|
19
|
+
<activity android:name="co.algorand.passkeyautofill.GetPasskeyActivity"
|
|
20
|
+
android:exported="true"
|
|
21
|
+
android:launchMode="singleTop"
|
|
22
|
+
android:theme="@style/Theme.PasskeyAutofill.Dialog">
|
|
20
23
|
<intent-filter>
|
|
21
24
|
<action android:name="co.algorand.passkeyautofill.GET_PASSKEY"/>
|
|
22
25
|
<category android:name="android.intent.category.DEFAULT"/>
|
|
23
26
|
</intent-filter>
|
|
24
27
|
</activity>
|
|
25
|
-
<activity android:name=".CreatePasskeyActivity"
|
|
28
|
+
<activity android:name="co.algorand.passkeyautofill.CreatePasskeyActivity"
|
|
29
|
+
android:exported="true"
|
|
30
|
+
android:launchMode="singleTop"
|
|
31
|
+
android:theme="@style/Theme.PasskeyAutofill.Dialog">
|
|
26
32
|
<intent-filter>
|
|
27
33
|
<action android:name="co.algorand.passkeyautofill.CREATE_PASSKEY"/>
|
|
28
34
|
<category android:name="android.intent.category.DEFAULT"/>
|