@algorandfoundation/react-native-passkey-autofill 1.0.0-canary.4 โ 1.0.0-canary.6
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/.eslintrc.js +3 -0
- package/.releaserc +23 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +10 -0
- package/README.md +29 -1
- package/android/build.gradle +18 -0
- package/android/src/main/AndroidManifest.xml +0 -1
- package/android/src/main/java/co/algorand/passkeyautofill/CreatePasskeyActivity.kt +25 -16
- package/android/src/main/java/co/algorand/passkeyautofill/GetPasskeyActivity.kt +23 -9
- package/android/src/main/java/co/algorand/passkeyautofill/service/PasskeyAutofillCredentialProviderService.kt +65 -34
- package/android/src/main/java/co/algorand/passkeyautofill/utils/PasskeyUtils.kt +7 -0
- package/assets/banner.png +0 -0
- package/ios/ReactNativePasskeyAutofillModule.swift +14 -15
- package/lefthook.yml +13 -0
- package/package.json +28 -8
package/.eslintrc.js
CHANGED
package/.releaserc
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": [
|
|
3
|
+
"release",
|
|
4
|
+
{
|
|
5
|
+
"name": "main",
|
|
6
|
+
"prerelease": "canary"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"plugins": [
|
|
10
|
+
"@semantic-release/commit-analyzer",
|
|
11
|
+
"@semantic-release/release-notes-generator",
|
|
12
|
+
"@semantic-release/changelog",
|
|
13
|
+
"@semantic-release/npm",
|
|
14
|
+
[
|
|
15
|
+
"@semantic-release/git",
|
|
16
|
+
{
|
|
17
|
+
"assets": ["package.json", "CHANGELOG.md"],
|
|
18
|
+
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"@semantic-release/github"
|
|
22
|
+
]
|
|
23
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# [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)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* include banner for package README ([60a6559](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/60a655932faac6de57daf0f30261f2d03a2c1476))
|
|
7
|
+
|
|
8
|
+
# [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)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **android:** add allowed credentials mapping ([e7134fc](https://github.com/algorandfoundation/react-native-passkey-autofill/commit/e7134fc1e5bf09649155e38010e4ef9728580812))
|
|
14
|
+
* **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
|
@@ -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
|
+
npm 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,7 +1,7 @@
|
|
|
1
1
|
# @algorandfoundation/react-native-passkey-autofill
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="./
|
|
4
|
+
<img src="./assets/banner.png" width="100%" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -66,6 +66,34 @@ await ReactNativePasskeyAutofill.configureIntentActions(
|
|
|
66
66
|
await ReactNativePasskeyAutofill.clearCredentials();
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
## ๐งช Testing
|
|
70
|
+
|
|
71
|
+
The project is set up with a comprehensive testing approach covering both JavaScript and Native (Kotlin) sides.
|
|
72
|
+
|
|
73
|
+
### JavaScript Tests
|
|
74
|
+
Run unit tests for the TypeScript module using Jest:
|
|
75
|
+
```bash
|
|
76
|
+
npm test
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Native Android Tests
|
|
80
|
+
Run unit tests for the Kotlin code using JUnit and Robolectric. These tests are executed via the example app's Gradle wrapper:
|
|
81
|
+
```bash
|
|
82
|
+
npm run test:android
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### All Tests
|
|
86
|
+
Run both JS and Native tests:
|
|
87
|
+
```bash
|
|
88
|
+
npm run test:all
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Continuous Integration (CI)
|
|
92
|
+
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`.
|
|
93
|
+
|
|
94
|
+
### Integration Testing (E2E)
|
|
95
|
+
For full end-to-end testing that covers the bridge between JavaScript and Native, we recommend using [Maestro](https://maestro.mobile.dev/). It provides a clean, YAML-based way to automate UI flows and verify the integration of the Passkey Autofill service with the system UI.
|
|
96
|
+
|
|
69
97
|
## ๐ฑ Example App
|
|
70
98
|
|
|
71
99
|
The [example](./example) app demonstrates how to integrate this module with a full wallet implementation.
|
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,6 +44,12 @@ 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
|
+
}
|
|
50
|
+
packagingOptions {
|
|
51
|
+
pickFirst '**/libc++_shared.so'
|
|
52
|
+
}
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
dependencies {
|
|
@@ -59,6 +66,17 @@ dependencies {
|
|
|
59
66
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
60
67
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0"
|
|
61
68
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0"
|
|
69
|
+
|
|
70
|
+
// Testing dependencies
|
|
71
|
+
testImplementation "junit:junit:4.13.2"
|
|
72
|
+
testImplementation "org.robolectric:robolectric:4.11.1"
|
|
73
|
+
testImplementation "io.mockk:mockk:1.13.8"
|
|
74
|
+
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0"
|
|
75
|
+
|
|
76
|
+
androidTestImplementation "androidx.test:runner:1.5.2"
|
|
77
|
+
androidTestImplementation "androidx.test:rules:1.5.0"
|
|
78
|
+
androidTestImplementation "androidx.test.ext:junit:1.1.5"
|
|
79
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
|
|
62
80
|
}
|
|
63
81
|
|
|
64
82
|
repositories {
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
android:enabled="true"
|
|
7
7
|
android:exported="true"
|
|
8
8
|
android:label="@string/passkey_autofill_label"
|
|
9
|
-
android:icon="@mipmap/ic_launcher"
|
|
10
9
|
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
|
|
11
10
|
tools:targetApi="upside_down_cake">
|
|
12
11
|
<intent-filter>
|
|
@@ -266,25 +266,34 @@ class CreatePasskeyActivity : AppCompatActivity() {
|
|
|
266
266
|
)
|
|
267
267
|
|
|
268
268
|
val resultIntent = Intent()
|
|
269
|
-
val
|
|
270
|
-
val
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
val
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
269
|
+
val fullJson = JSONObject(fidoCredential.json())
|
|
270
|
+
val respJson = fullJson.getJSONObject("response")
|
|
271
|
+
|
|
272
|
+
// Ensure compact clientDataJSON for registration too
|
|
273
|
+
val sanitizedOrigin = credentialRepository.getOrigin(req.callingAppInfo).replace(Regex("/$"), "")
|
|
274
|
+
val challenge = if (requestJson.has("publicKey")) {
|
|
275
|
+
requestJson.getJSONObject("publicKey").getString("challenge")
|
|
276
|
+
} else {
|
|
277
|
+
requestJson.getString("challenge")
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
val clientDataJSONString = if (sanitizedOrigin.startsWith("https://") || sanitizedOrigin.startsWith("http://")) {
|
|
281
|
+
"{\"type\":\"webauthn.create\",\"challenge\":\"$challenge\",\"origin\":\"$sanitizedOrigin\",\"crossOrigin\":false}"
|
|
282
|
+
} else {
|
|
283
|
+
val json = JSONObject()
|
|
284
|
+
json.put("type", "webauthn.create")
|
|
285
|
+
json.put("challenge", challenge)
|
|
286
|
+
json.put("origin", sanitizedOrigin)
|
|
287
|
+
json.put("crossOrigin", false)
|
|
288
|
+
if (sanitizedOrigin.startsWith("android:apk-key-hash:")) {
|
|
289
|
+
json.put("androidPackageName", req.callingAppInfo.packageName)
|
|
279
290
|
}
|
|
280
|
-
|
|
291
|
+
json.toString()
|
|
281
292
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
val y = to32(ecPubKey.w.affineY)
|
|
285
|
-
val s = to32(ecPrivKey.s)
|
|
293
|
+
val clientDataJSONb64 = AndroidBase64.encodeToString(clientDataJSONString.toByteArray(), AndroidBase64.URL_SAFE or AndroidBase64.NO_WRAP or AndroidBase64.NO_PADDING)
|
|
294
|
+
respJson.put("clientDataJSON", clientDataJSONb64)
|
|
286
295
|
|
|
287
|
-
val createResponse = CreatePublicKeyCredentialResponse(
|
|
296
|
+
val createResponse = CreatePublicKeyCredentialResponse(fullJson.toString())
|
|
288
297
|
|
|
289
298
|
PendingIntentHandler.setCreateCredentialResponse(
|
|
290
299
|
resultIntent,
|
|
@@ -229,17 +229,31 @@ class GetPasskeyActivity : AppCompatActivity() {
|
|
|
229
229
|
}
|
|
230
230
|
val sanitizedOrigin = origin.replace(Regex("/$"), "")
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
232
|
+
// Look for system-provided clientDataHash (e.g. from Chrome)
|
|
233
|
+
val systemClientDataHash = run {
|
|
234
|
+
val option = req.credentialOptions.find { opt ->
|
|
235
|
+
opt is GetPublicKeyCredentialOption && (opt.requestJson == rawRequestJson ||
|
|
236
|
+
try { JSONObject(opt.requestJson).toString() == JSONObject(rawRequestJson!!).toString() } catch(e: Exception) { false })
|
|
237
|
+
} as? GetPublicKeyCredentialOption
|
|
238
|
+
option?.requestData?.getByteArray("androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH")
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
val clientDataJSONString =
|
|
242
|
-
|
|
241
|
+
val clientDataJSONString = if (sanitizedOrigin.startsWith("https://") || sanitizedOrigin.startsWith("http://")) {
|
|
242
|
+
// Compact JSON for web origins to match browser hashing (no spaces, specific order)
|
|
243
|
+
"{\"type\":\"webauthn.get\",\"challenge\":\"$challenge\",\"origin\":\"$sanitizedOrigin\",\"crossOrigin\":false}"
|
|
244
|
+
} else {
|
|
245
|
+
val json = JSONObject()
|
|
246
|
+
json.put("type", "webauthn.get")
|
|
247
|
+
json.put("challenge", challenge)
|
|
248
|
+
json.put("origin", sanitizedOrigin)
|
|
249
|
+
json.put("crossOrigin", false)
|
|
250
|
+
if (sanitizedOrigin.startsWith("android:apk-key-hash:")) {
|
|
251
|
+
json.put("androidPackageName", req.callingAppInfo.packageName)
|
|
252
|
+
}
|
|
253
|
+
json.toString()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
val clientDataHash = systemClientDataHash ?: MessageDigest.getInstance("SHA-256").digest(clientDataJSONString.toByteArray(Charsets.UTF_8))
|
|
243
257
|
|
|
244
258
|
val dbCred = credentialRepository.getCredential(this, credId)
|
|
245
259
|
?: throw IllegalStateException("Credential not found")
|
|
@@ -26,8 +26,10 @@ import androidx.credentials.provider.ProviderClearCredentialStateRequest
|
|
|
26
26
|
import androidx.credentials.provider.PublicKeyCredentialEntry
|
|
27
27
|
import co.algorand.passkeyautofill.credentials.CredentialRepository
|
|
28
28
|
import co.algorand.passkeyautofill.credentials.Credential
|
|
29
|
+
import co.algorand.passkeyautofill.utils.PasskeyUtils
|
|
29
30
|
import com.tencent.mmkv.MMKV
|
|
30
31
|
import kotlinx.coroutines.*
|
|
32
|
+
import org.json.JSONArray
|
|
31
33
|
import org.json.JSONObject
|
|
32
34
|
|
|
33
35
|
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
@@ -127,7 +129,7 @@ class PasskeyAutofillCredentialProviderService: CredentialProviderService() {
|
|
|
127
129
|
/**
|
|
128
130
|
* Fake a list of available PublicKeyCredential Entries
|
|
129
131
|
*/
|
|
130
|
-
private fun processGetCredentialRequest(request: BeginGetCredentialRequest): BeginGetCredentialResponse{
|
|
132
|
+
private fun processGetCredentialRequest(request: BeginGetCredentialRequest): BeginGetCredentialResponse {
|
|
131
133
|
// Ensure we have a master key available. If not, we can't sign anything, so don't show entries.
|
|
132
134
|
if (!credentialRepository.isMasterKeyAvailable(this)) {
|
|
133
135
|
return BeginGetCredentialResponse(emptyList())
|
|
@@ -138,42 +140,66 @@ class PasskeyAutofillCredentialProviderService: CredentialProviderService() {
|
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
val action = credentialRepository.getGetPasskeyAction(this) ?: DEFAULT_GET_PASSKEY_ACTION
|
|
143
|
+
val allEntries = mutableListOf<PublicKeyCredentialEntry>()
|
|
141
144
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// Find suitable option for this entry
|
|
145
|
-
val option = request.beginGetCredentialOptions.find { opt ->
|
|
146
|
-
if (opt !is BeginGetPublicKeyCredentialOption) return@find false
|
|
147
|
-
|
|
148
|
-
// In a real app, we'd check if this credential's origin matches the request's origin
|
|
149
|
-
// For now, we return all credentials that match the requested type.
|
|
150
|
-
true
|
|
151
|
-
} as? BeginGetPublicKeyCredentialOption
|
|
152
|
-
|
|
153
|
-
if (option == null) {
|
|
154
|
-
return@mapNotNull null
|
|
155
|
-
}
|
|
145
|
+
for (option in request.beginGetCredentialOptions) {
|
|
146
|
+
if (option !is BeginGetPublicKeyCredentialOption) continue
|
|
156
147
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
this@PasskeyAutofillCredentialProviderService,
|
|
164
|
-
credential.userHandle,
|
|
165
|
-
createNewPendingIntent(action, GET_PASSKEY_INTENT, data),
|
|
166
|
-
option
|
|
167
|
-
)
|
|
168
|
-
.setIcon(Icon.createWithResource(this@PasskeyAutofillCredentialProviderService, android.R.drawable.ic_lock_lock))
|
|
169
|
-
.build()
|
|
148
|
+
val requestJsonStr = option.requestJson
|
|
149
|
+
var allowCredentials: JSONArray? = null
|
|
150
|
+
try {
|
|
151
|
+
val json = JSONObject(requestJsonStr)
|
|
152
|
+
val pk = if (json.has("publicKey")) json.getJSONObject("publicKey") else json
|
|
153
|
+
allowCredentials = pk.optJSONArray("allowCredentials")
|
|
170
154
|
} catch (e: Exception) {
|
|
171
|
-
Log.e(TAG, "Error
|
|
155
|
+
Log.e(TAG, "Error parsing requestJson", e)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
val allowedIds = if (allowCredentials != null && allowCredentials.length() > 0) {
|
|
159
|
+
val ids = mutableSetOf<String>()
|
|
160
|
+
for (i in 0 until allowCredentials.length()) {
|
|
161
|
+
ids.add(allowCredentials.getJSONObject(i).getString("id"))
|
|
162
|
+
}
|
|
163
|
+
ids
|
|
164
|
+
} else {
|
|
172
165
|
null
|
|
173
166
|
}
|
|
167
|
+
|
|
168
|
+
for (credential in credentials) {
|
|
169
|
+
if (allowedIds != null) {
|
|
170
|
+
val isAllowed = allowedIds.any { allowedId ->
|
|
171
|
+
allowedId == credential.credentialId ||
|
|
172
|
+
PasskeyUtils.normalizeBase64(allowedId) == PasskeyUtils.normalizeBase64(credential.credentialId)
|
|
173
|
+
}
|
|
174
|
+
if (!isAllowed) continue
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
val data = Bundle()
|
|
179
|
+
data.putString("credentialId", credential.credentialId)
|
|
180
|
+
data.putString("userHandle", credential.userHandle)
|
|
181
|
+
data.putString("requestJson", option.requestJson)
|
|
182
|
+
|
|
183
|
+
// Use a unique requestCode for each PendingIntent to avoid data being overwritten
|
|
184
|
+
val uniqueRequestCode = (credential.credentialId + option.requestJson).hashCode()
|
|
185
|
+
|
|
186
|
+
val entry = PublicKeyCredentialEntry.Builder(
|
|
187
|
+
this,
|
|
188
|
+
credential.userHandle,
|
|
189
|
+
createNewPendingIntent(action, uniqueRequestCode, data),
|
|
190
|
+
option
|
|
191
|
+
)
|
|
192
|
+
.setIcon(Icon.createWithResource(this, android.R.drawable.ic_lock_lock))
|
|
193
|
+
.build()
|
|
194
|
+
allEntries.add(entry)
|
|
195
|
+
} catch (e: Exception) {
|
|
196
|
+
Log.e(TAG, "Error building PublicKeyCredentialEntry", e)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
174
199
|
}
|
|
175
|
-
return BeginGetCredentialResponse(
|
|
200
|
+
return BeginGetCredentialResponse(allEntries)
|
|
176
201
|
}
|
|
202
|
+
|
|
177
203
|
override fun onClearCredentialStateRequest(
|
|
178
204
|
request: ProviderClearCredentialStateRequest,
|
|
179
205
|
cancellationSignal: CancellationSignal,
|
|
@@ -186,12 +212,17 @@ class PasskeyAutofillCredentialProviderService: CredentialProviderService() {
|
|
|
186
212
|
private fun createNewPendingIntent(action: String, requestCode: Int, extra: Bundle?): PendingIntent{
|
|
187
213
|
MMKV.initialize(this)
|
|
188
214
|
val intent = Intent(action)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
215
|
+
|
|
216
|
+
// Determine component based on action instead of requestCode
|
|
217
|
+
val getAction = credentialRepository.getGetPasskeyAction(this) ?: DEFAULT_GET_PASSKEY_ACTION
|
|
218
|
+
val createAction = credentialRepository.getCreatePasskeyAction(this) ?: DEFAULT_CREATE_PASSKEY_ACTION
|
|
219
|
+
|
|
220
|
+
val componentName = when(action) {
|
|
221
|
+
getAction -> android.content.ComponentName(packageName, "co.algorand.passkeyautofill.GetPasskeyActivity")
|
|
222
|
+
createAction -> android.content.ComponentName(packageName, "co.algorand.passkeyautofill.CreatePasskeyActivity")
|
|
193
223
|
else -> null
|
|
194
224
|
}
|
|
225
|
+
|
|
195
226
|
if (componentName != null) {
|
|
196
227
|
intent.component = componentName
|
|
197
228
|
} else {
|
|
Binary file
|
|
@@ -10,26 +10,25 @@ public class ReactNativePasskeyAutofillModule: Module {
|
|
|
10
10
|
// The module will be accessible from `requireNativeModule('ReactNativePasskeyAutofill')` in JavaScript.
|
|
11
11
|
Name("ReactNativePasskeyAutofill")
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Double.pi
|
|
13
|
+
AsyncFunction("setMasterKey") { (secret: String) in
|
|
14
|
+
// TODO: Implement for iOS
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
AsyncFunction("setHdRootKeyId") { (id: String) in
|
|
18
|
+
// TODO: Implement for iOS
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
AsyncFunction("getHdRootKeyId") { () -> String? in
|
|
22
|
+
// TODO: Implement for iOS
|
|
23
|
+
return nil
|
|
24
|
+
}
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return "Hello world! ๐"
|
|
26
|
+
AsyncFunction("clearCredentials") {
|
|
27
|
+
// TODO: Implement for iOS
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
AsyncFunction("setValueAsync") { (value: String) in
|
|
29
|
-
// Send an event to JavaScript.
|
|
30
|
-
self.sendEvent("onChange", [
|
|
31
|
-
"value": value
|
|
32
|
-
])
|
|
30
|
+
AsyncFunction("configureIntentActions") { (getPasskeyAction: String, createPasskeyAction: String) in
|
|
31
|
+
// TODO: Implement for iOS
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
// Enables the module to be used as a native view. Definition components that are accepted as part of the
|
package/lefthook.yml
ADDED
package/package.json
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@algorandfoundation/react-native-passkey-autofill",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.6",
|
|
4
4
|
"description": "Passkey AutoFill React Native",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"publishConfig": {
|
|
8
|
-
"access": "public"
|
|
8
|
+
"access": "public",
|
|
9
|
+
"provenance": true
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"build": "expo-module build",
|
|
12
13
|
"clean": "expo-module clean",
|
|
13
|
-
"lint": "expo-module lint",
|
|
14
|
-
"test": "expo-module test",
|
|
15
|
-
"
|
|
14
|
+
"lint": "ESLINT_USE_FLAT_CONFIG=false expo-module lint",
|
|
15
|
+
"test": "CI=true expo-module test",
|
|
16
|
+
"test:android": "cd example/android && ./gradlew :algorandfoundation-react-native-passkey-autofill:testDebugUnitTest --no-daemon",
|
|
17
|
+
"test:android:instrumented": "cd example/android && ./gradlew :algorandfoundation-react-native-passkey-autofill:connectedDebugAndroidTest --no-daemon",
|
|
18
|
+
"test:all": "npm run test && npm run test:android",
|
|
19
|
+
"prepare": "expo-module prepare && npx lefthook install",
|
|
16
20
|
"prepublishOnly": "expo-module prepublishOnly",
|
|
17
21
|
"expo-module": "expo-module",
|
|
18
22
|
"open:ios": "xed example/ios",
|
|
@@ -24,22 +28,38 @@
|
|
|
24
28
|
"@algorandfoundation/react-native-passkey-autofill",
|
|
25
29
|
"ReactNativePasskeyAutofill"
|
|
26
30
|
],
|
|
27
|
-
"repository":
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+ssh://git@github.com/algorandfoundation/react-native-passkey-autofill.git"
|
|
34
|
+
},
|
|
28
35
|
"bugs": {
|
|
29
36
|
"url": "https://github.com/algorandfoundation/react-native-passkey-autofill/issues"
|
|
30
37
|
},
|
|
31
|
-
"author": "Algorand Foundation
|
|
38
|
+
"author": "Algorand Foundation",
|
|
32
39
|
"license": "Apache-2.0",
|
|
33
40
|
"homepage": "https://github.com/algorandfoundation/react-native-passkey-autofill#readme",
|
|
34
41
|
"dependencies": {
|
|
35
42
|
"@algorandfoundation/react-native-keystore": "^1.0.0-canary.6"
|
|
36
43
|
},
|
|
44
|
+
"jest": {
|
|
45
|
+
"preset": "ts-jest",
|
|
46
|
+
"testEnvironment": "node",
|
|
47
|
+
"testMatch": [
|
|
48
|
+
"<rootDir>/src/**/__tests__/**/*.test.ts"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
37
51
|
"devDependencies": {
|
|
38
52
|
"@expo/config-plugins": "^9.0.0",
|
|
53
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
54
|
+
"@semantic-release/git": "^10.0.1",
|
|
55
|
+
"@semantic-release/github": "^12.0.6",
|
|
56
|
+
"@semantic-release/npm": "^13.1.5",
|
|
39
57
|
"@types/react": "~19.1.1",
|
|
40
58
|
"expo": "^55.0.1",
|
|
41
59
|
"expo-module-scripts": "^55.0.2",
|
|
42
|
-
"
|
|
60
|
+
"lefthook": "^2.1.4",
|
|
61
|
+
"react-native": "0.82.1",
|
|
62
|
+
"semantic-release": "^25.0.3"
|
|
43
63
|
},
|
|
44
64
|
"peerDependencies": {
|
|
45
65
|
"expo": "*",
|