@algorandfoundation/react-native-passkey-autofill 0.0.1 → 1.0.0-canary.10

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 (45) hide show
  1. package/.eslintrc.js +8 -0
  2. package/.releaserc +23 -0
  3. package/ARCHITECTURE.md +67 -0
  4. package/CHANGELOG.md +42 -0
  5. package/CONTRIBUTING.md +82 -0
  6. package/LICENSE +201 -0
  7. package/README.md +135 -4
  8. package/android/build.gradle +86 -0
  9. package/android/libs/dP256Android-release.aar +0 -0
  10. package/android/src/main/AndroidManifest.xml +37 -0
  11. package/android/src/main/java/co/algorand/passkeyautofill/CreatePasskeyActivity.kt +318 -0
  12. package/android/src/main/java/co/algorand/passkeyautofill/GetPasskeyActivity.kt +327 -0
  13. package/android/src/main/java/co/algorand/passkeyautofill/ReactNativePasskeyAutofillModule.kt +84 -0
  14. package/android/src/main/java/co/algorand/passkeyautofill/credentials/Credential.kt +11 -0
  15. package/android/src/main/java/co/algorand/passkeyautofill/credentials/CredentialRepository.kt +506 -0
  16. package/android/src/main/java/co/algorand/passkeyautofill/service/PasskeyAutofillCredentialProviderService.kt +248 -0
  17. package/android/src/main/java/co/algorand/passkeyautofill/utils/PasskeyUtils.kt +7 -0
  18. package/android/src/main/res/values/strings.xml +3 -0
  19. package/android/src/main/res/values/styles.xml +10 -0
  20. package/android/src/main/res/xml/provider.xml +5 -0
  21. package/app.plugin.js +249 -0
  22. package/assets/banner.png +0 -0
  23. package/build/ReactNativePasskeyAutofill.types.d.ts +9 -0
  24. package/build/ReactNativePasskeyAutofill.types.d.ts.map +1 -0
  25. package/build/ReactNativePasskeyAutofill.types.js +2 -0
  26. package/build/ReactNativePasskeyAutofill.types.js.map +1 -0
  27. package/build/ReactNativePasskeyAutofillModule.d.ts +12 -0
  28. package/build/ReactNativePasskeyAutofillModule.d.ts.map +1 -0
  29. package/build/ReactNativePasskeyAutofillModule.js +4 -0
  30. package/build/ReactNativePasskeyAutofillModule.js.map +1 -0
  31. package/build/index.d.ts +3 -0
  32. package/build/index.d.ts.map +1 -0
  33. package/build/index.js +5 -0
  34. package/build/index.js.map +1 -0
  35. package/expo-module.config.json +10 -0
  36. package/ios/ReactNativePasskeyAutofill.podspec +29 -0
  37. package/ios/ReactNativePasskeyAutofillModule.swift +49 -0
  38. package/ios/ReactNativePasskeyAutofillView.swift +38 -0
  39. package/lefthook.yml +13 -0
  40. package/package.json +64 -8
  41. package/src/ReactNativePasskeyAutofill.types.ts +4 -0
  42. package/src/ReactNativePasskeyAutofillModule.ts +14 -0
  43. package/src/index.ts +4 -0
  44. package/tsconfig.json +9 -0
  45. package/index.js +0 -1
@@ -0,0 +1,248 @@
1
+ package co.algorand.passkeyautofill.service
2
+
3
+ import android.app.PendingIntent
4
+ import android.content.Intent
5
+ import android.graphics.drawable.Icon
6
+ import android.os.Build
7
+ import android.os.Bundle
8
+ import android.os.OutcomeReceiver
9
+ import android.os.CancellationSignal
10
+ import android.util.Log
11
+ import androidx.annotation.RequiresApi
12
+ import androidx.credentials.exceptions.ClearCredentialException
13
+ import androidx.credentials.exceptions.CreateCredentialException
14
+ import androidx.credentials.exceptions.CreateCredentialUnknownException
15
+ import androidx.credentials.exceptions.GetCredentialException
16
+ import androidx.credentials.exceptions.GetCredentialUnknownException
17
+ import androidx.credentials.provider.BeginCreateCredentialRequest
18
+ import androidx.credentials.provider.BeginCreateCredentialResponse
19
+ import androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest
20
+ import androidx.credentials.provider.BeginGetCredentialRequest
21
+ import androidx.credentials.provider.BeginGetCredentialResponse
22
+ import androidx.credentials.provider.BeginGetPublicKeyCredentialOption
23
+ import androidx.credentials.provider.CreateEntry
24
+ import androidx.credentials.provider.CredentialProviderService
25
+ import androidx.credentials.provider.ProviderClearCredentialStateRequest
26
+ import androidx.credentials.provider.PublicKeyCredentialEntry
27
+ import co.algorand.passkeyautofill.credentials.CredentialRepository
28
+ import co.algorand.passkeyautofill.credentials.Credential
29
+ import co.algorand.passkeyautofill.utils.PasskeyUtils
30
+ import com.tencent.mmkv.MMKV
31
+ import kotlinx.coroutines.*
32
+ import org.json.JSONArray
33
+ import org.json.JSONObject
34
+
35
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
36
+ class PasskeyAutofillCredentialProviderService: CredentialProviderService() {
37
+ private val credentialRepository = CredentialRepository()
38
+ private val job = SupervisorJob()
39
+ private val scope = CoroutineScope(Dispatchers.IO + job)
40
+
41
+ companion object {
42
+ const val TAG = "PasskeyAutofillCredentialProviderService"
43
+ //TODO: App Lock Intents
44
+ const val GET_PASSKEY_INTENT = 1
45
+ const val CREATE_PASSKEY_INTENT = 2
46
+ const val DEFAULT_GET_PASSKEY_ACTION = "co.algorand.passkeyautofill.GET_PASSKEY"
47
+ const val DEFAULT_CREATE_PASSKEY_ACTION = "co.algorand.passkeyautofill.CREATE_PASSKEY"
48
+ }
49
+ /**
50
+ * Handle Create Credential Requests
51
+ */
52
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
53
+ override fun onBeginCreateCredentialRequest(
54
+ request: BeginCreateCredentialRequest,
55
+ cancellationSignal: CancellationSignal,
56
+ callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>
57
+ ) {
58
+ val response: BeginCreateCredentialResponse? = processCreateCredentialRequest(request)
59
+ if (response != null) {
60
+ callback.onResult(response)
61
+ } else {
62
+ callback.onError(CreateCredentialUnknownException())
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Process incoming Create Credential Requests
68
+ */
69
+ private fun processCreateCredentialRequest(request: BeginCreateCredentialRequest): BeginCreateCredentialResponse? {
70
+ when (request) {
71
+ is BeginCreatePublicKeyCredentialRequest -> {
72
+ return handleCreatePasskeyQuery(request)
73
+ }
74
+ }
75
+ return null
76
+ }
77
+
78
+ /**
79
+ * Create a new PassKey Entry
80
+ *
81
+ * This returns an Entry list for the user to interact with.
82
+ * A PendingIntent must be configured to receive the data from the WebAuthn client
83
+ */
84
+ private fun handleCreatePasskeyQuery(
85
+ request: BeginCreatePublicKeyCredentialRequest
86
+ ): BeginCreateCredentialResponse {
87
+ val createEntries: MutableList<CreateEntry> = mutableListOf()
88
+
89
+ // Ensure we have a master key available before offering to create a passkey
90
+ if (!credentialRepository.isMasterKeyAvailable(this)) {
91
+ return BeginCreateCredentialResponse(createEntries)
92
+ }
93
+
94
+ val userJson = JSONObject(request.requestJson).optJSONObject("user")
95
+ val name = userJson?.optString("name") ?: "New Passkey"
96
+
97
+ val action = credentialRepository.getCreatePasskeyAction(this) ?: DEFAULT_CREATE_PASSKEY_ACTION
98
+
99
+ val data = Bundle()
100
+ data.putString("requestJson", request.requestJson)
101
+
102
+ val createEntry = CreateEntry.Builder(
103
+ name,
104
+ createNewPendingIntent(action, CREATE_PASSKEY_INTENT, data)
105
+ ).setIcon(Icon.createWithResource(this, android.R.drawable.ic_menu_add))
106
+ .build()
107
+
108
+ createEntries.add(createEntry)
109
+ return BeginCreateCredentialResponse(createEntries)
110
+ }
111
+ /**
112
+ * Handle Get Credential Requests
113
+ */
114
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
115
+ override fun onBeginGetCredentialRequest(
116
+ request: BeginGetCredentialRequest,
117
+ cancellationSignal: CancellationSignal,
118
+ callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>,
119
+ ) {
120
+ try {
121
+ val response = processGetCredentialRequest(request)
122
+ callback.onResult(response)
123
+ } catch (e: Exception) {
124
+ Log.e(TAG, "Error in onBeginGetCredentialRequest", e)
125
+ callback.onError(GetCredentialUnknownException())
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Fake a list of available PublicKeyCredential Entries
131
+ */
132
+ private fun processGetCredentialRequest(request: BeginGetCredentialRequest): BeginGetCredentialResponse {
133
+ // Ensure we have a master key available. If not, we can't sign anything, so don't show entries.
134
+ if (!credentialRepository.isMasterKeyAvailable(this)) {
135
+ return BeginGetCredentialResponse(emptyList())
136
+ }
137
+
138
+ val credentials = runBlocking {
139
+ credentialRepository.getAllCredentials(this@PasskeyAutofillCredentialProviderService)
140
+ }
141
+
142
+ val action = credentialRepository.getGetPasskeyAction(this) ?: DEFAULT_GET_PASSKEY_ACTION
143
+ val allEntries = mutableListOf<PublicKeyCredentialEntry>()
144
+
145
+ for (option in request.beginGetCredentialOptions) {
146
+ if (option !is BeginGetPublicKeyCredentialOption) continue
147
+
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")
154
+ } catch (e: Exception) {
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 {
165
+ null
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
+ }
199
+ }
200
+ return BeginGetCredentialResponse(allEntries)
201
+ }
202
+
203
+ override fun onClearCredentialStateRequest(
204
+ request: ProviderClearCredentialStateRequest,
205
+ cancellationSignal: CancellationSignal,
206
+ callback: OutcomeReceiver<Void?, ClearCredentialException>
207
+ ) {
208
+ // TODO: Implement clear credential state if needed
209
+ callback.onResult(null)
210
+ }
211
+
212
+ private fun createNewPendingIntent(action: String, requestCode: Int, extra: Bundle?): PendingIntent{
213
+ MMKV.initialize(this)
214
+ val intent = Intent(action)
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")
223
+ else -> null
224
+ }
225
+
226
+ if (componentName != null) {
227
+ intent.component = componentName
228
+ } else {
229
+ intent.setPackage(packageName)
230
+ }
231
+
232
+ if (extra != null) {
233
+ intent.putExtra("CREDENTIAL_DATA", extra)
234
+ }
235
+
236
+ // Add categories that might be required
237
+ intent.addCategory(android.content.Intent.CATEGORY_DEFAULT)
238
+
239
+ return PendingIntent.getActivity(
240
+ applicationContext, requestCode,
241
+ intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
242
+ )
243
+ }
244
+ override fun onDestroy() {
245
+ super.onDestroy()
246
+ job.cancel()
247
+ }
248
+ }
@@ -0,0 +1,7 @@
1
+ package co.algorand.passkeyautofill.utils
2
+
3
+ object PasskeyUtils {
4
+ fun normalizeBase64(s: String): String {
5
+ return s.replace("-", "+").replace("_", "/").replace("=", "")
6
+ }
7
+ }
@@ -0,0 +1,3 @@
1
+ <resources>
2
+ <string name="passkey_autofill_label">My Credential Provider</string>
3
+ </resources>
@@ -0,0 +1,10 @@
1
+ <resources>
2
+ <style name="Theme.PasskeyAutofill.Dialog" parent="Theme.AppCompat.Light.Dialog.MinWidth">
3
+ <item name="android:windowIsTranslucent">true</item>
4
+ <item name="android:windowBackground">@android:color/transparent</item>
5
+ <item name="android:windowContentOverlay">@null</item>
6
+ <item name="android:windowNoTitle">true</item>
7
+ <item name="android:windowIsFloating">true</item>
8
+ <item name="android:backgroundDimEnabled">true</item>
9
+ </style>
10
+ </resources>
@@ -0,0 +1,5 @@
1
+ <credential-provider xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <capabilities>
3
+ <capability name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
4
+ </capabilities>
5
+ </credential-provider>
package/app.plugin.js ADDED
@@ -0,0 +1,249 @@
1
+ const {
2
+ withAndroidManifest,
3
+ withStringsXml,
4
+ withProjectBuildGradle,
5
+ withMainApplication,
6
+ withDangerousMod,
7
+ AndroidConfig,
8
+ } = require("@expo/config-plugins");
9
+ const path = require("path");
10
+ const fs = require("fs");
11
+
12
+ const withAndroidCookieModule = (config) => {
13
+ return withDangerousMod(config, [
14
+ "android",
15
+ async (config) => {
16
+ const projectRoot = config.modRequest.projectRoot;
17
+ const packageId = config.android?.package || "co.algorand.auth.example";
18
+ const packagePath = packageId.replace(/\./g, "/");
19
+ const targetDir = path.join(
20
+ projectRoot,
21
+ "android/app/src/main/java",
22
+ packagePath
23
+ );
24
+
25
+ // Ensure the directory exists
26
+ fs.mkdirSync(targetDir, { recursive: true });
27
+
28
+ const cookieModuleContent = `package ${packageId}
29
+
30
+ import com.facebook.react.bridge.ReactApplicationContext
31
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
32
+ import com.facebook.react.bridge.ReactMethod
33
+ import com.facebook.react.bridge.Promise
34
+ import android.webkit.CookieManager
35
+
36
+ class CookieModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
37
+ override fun getName(): String {
38
+ return "CookieModule"
39
+ }
40
+
41
+ @ReactMethod
42
+ fun getCookie(url: String, promise: Promise) {
43
+ try {
44
+ val cookieManager = CookieManager.getInstance()
45
+ val cookie = cookieManager.getCookie(url)
46
+ promise.resolve(cookie)
47
+ } catch (e: Exception) {
48
+ promise.reject("E_COOKIE_MANAGER", e.message)
49
+ }
50
+ }
51
+
52
+ @ReactMethod
53
+ fun setCookie(url: String, cookie: String, promise: Promise) {
54
+ try {
55
+ val cookieManager = CookieManager.getInstance()
56
+ cookieManager.setCookie(url, cookie)
57
+ promise.resolve(null)
58
+ } catch (e: Exception) {
59
+ promise.reject("E_COOKIE_MANAGER", e.message)
60
+ }
61
+ }
62
+ }
63
+ `;
64
+
65
+ const cookiePackageContent = `package ${packageId}
66
+
67
+ import com.facebook.react.ReactPackage
68
+ import com.facebook.react.bridge.NativeModule
69
+ import com.facebook.react.bridge.ReactApplicationContext
70
+ import com.facebook.react.uimanager.ViewManager
71
+
72
+ class CookiePackage : ReactPackage {
73
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
74
+ return listOf(CookieModule(reactContext))
75
+ }
76
+
77
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
78
+ return emptyList()
79
+ }
80
+ }
81
+ `;
82
+
83
+ fs.writeFileSync(
84
+ path.join(targetDir, "CookieModule.kt"),
85
+ cookieModuleContent
86
+ );
87
+ fs.writeFileSync(
88
+ path.join(targetDir, "CookiePackage.kt"),
89
+ cookiePackageContent
90
+ );
91
+
92
+ return config;
93
+ },
94
+ ]);
95
+ };
96
+
97
+ const withUserAgent = (config) => {
98
+ return withMainApplication(config, (config) => {
99
+ let content = config.modResults.contents;
100
+
101
+ // Add imports
102
+ const imports = [
103
+ "import android.webkit.CookieManager",
104
+ "import com.facebook.react.modules.network.OkHttpClientProvider",
105
+ "import com.facebook.react.modules.network.ForwardingCookieHandler",
106
+ "import com.facebook.react.modules.network.ReactCookieJarContainer",
107
+ "import okhttp3.Interceptor",
108
+ "import okhttp3.JavaNetCookieJar",
109
+ "import java.net.CookieHandler",
110
+ "import android.os.Build",
111
+ ];
112
+
113
+ imports.forEach((imp) => {
114
+ if (!content.includes(imp)) {
115
+ content = content.replace(
116
+ /package .*\n/,
117
+ (match) => `${match}${imp}\n`
118
+ );
119
+ }
120
+ });
121
+
122
+ // Add OkHttpClient customization in onCreate
123
+ const okHttpClientCode = `
124
+ CookieManager.getInstance().setAcceptCookie(true)
125
+ CookieManager.setAcceptFileSchemeCookies(true)
126
+
127
+ OkHttpClientProvider.setOkHttpClientFactory {
128
+ val userAgent = "\${BuildConfig.APPLICATION_ID}/\${BuildConfig.VERSION_NAME} " +
129
+ "(Android \${Build.VERSION.RELEASE}; \${Build.MODEL}; \${Build.BRAND})"
130
+
131
+ val cookieHandler = ForwardingCookieHandler()
132
+ CookieHandler.setDefault(cookieHandler)
133
+
134
+ val cookieJarContainer = ReactCookieJarContainer()
135
+ cookieJarContainer.setCookieJar(JavaNetCookieJar(cookieHandler))
136
+
137
+ OkHttpClientProvider.createClientBuilder()
138
+ .cookieJar(cookieJarContainer)
139
+ .addInterceptor(Interceptor { chain ->
140
+ val request = chain.request().newBuilder().header("User-Agent", userAgent).build()
141
+ chain.proceed(request)
142
+ })
143
+ .build()
144
+ }
145
+ `;
146
+
147
+ if (!content.includes("OkHttpClientProvider.setOkHttpClientFactory")) {
148
+ content = content.replace(
149
+ /super\.onCreate\(\)/,
150
+ `super.onCreate()${okHttpClientCode}`
151
+ );
152
+ }
153
+
154
+ // Register CookiePackage if it exists
155
+ if (!content.includes("add(CookiePackage())")) {
156
+ content = content.replace(
157
+ /PackageList\(this\)\.packages\.apply \{/,
158
+ `PackageList(this).packages.apply {\n add(CookiePackage())`
159
+ );
160
+ }
161
+
162
+ config.modResults.contents = content;
163
+ return config;
164
+ });
165
+ };
166
+
167
+ const withPasskeyAutofill = (config, props = {}) => {
168
+ const site = props.site || "https://fido.shore-tech.net";
169
+ const label = props.label || "My Credential Provider";
170
+
171
+ config = withAndroidCookieModule(config);
172
+ config = withUserAgent(config);
173
+
174
+ // TODO: Move assetlinks to CLI
175
+ // 1. Add asset_statements meta-data to MainActivity
176
+ config = withAndroidManifest(config, async (config) => {
177
+ const mainActivity = config.modResults.manifest.application[0].activity.find(
178
+ (a) => a["$"]["android:name"] === ".MainActivity"
179
+ );
180
+ if (mainActivity) {
181
+ if (!mainActivity["meta-data"]) {
182
+ mainActivity["meta-data"] = [];
183
+ }
184
+ if (
185
+ !mainActivity["meta-data"].find(
186
+ (m) => m["$"]["android:name"] === "asset_statements"
187
+ )
188
+ ) {
189
+ mainActivity["meta-data"].push({
190
+ $: {
191
+ "android:name": "asset_statements",
192
+ "android:resource": "@string/asset_statements",
193
+ },
194
+ });
195
+ }
196
+ }
197
+ return config;
198
+ });
199
+
200
+ // 2. Add asset_statements string to strings.xml
201
+ config = withStringsXml(config, (config) => {
202
+ config.modResults = AndroidConfig.Strings.setStringItem(
203
+ [
204
+ {
205
+ $: { name: "asset_statements", translatable: "false" },
206
+ _: JSON.stringify([
207
+ {
208
+ relation: [
209
+ "delegate_permission/common.handle_all_urls",
210
+ "delegate_permission/common.get_login_creds",
211
+ ],
212
+ target: {
213
+ namespace: "web",
214
+ site: site,
215
+ },
216
+ },
217
+ ]),
218
+ },
219
+ {
220
+ $: { name: "passkey_autofill_label", translatable: "true" },
221
+ _: label,
222
+ },
223
+ ],
224
+ config.modResults
225
+ );
226
+ return config;
227
+ });
228
+
229
+ // 3. Add flatDir repository for local AAR
230
+ config = withProjectBuildGradle(config, (config) => {
231
+ if (config.modResults.contents.includes("dP256Android-release")) {
232
+ return config;
233
+ }
234
+ const libsDir = "../../android/libs";
235
+ config.modResults.contents = config.modResults.contents.replace(
236
+ /allprojects\s*{[\s\n]*repositories\s*{/,
237
+ `allprojects {
238
+ repositories {
239
+ flatDir {
240
+ dirs "$rootDir/${libsDir}"
241
+ }`
242
+ );
243
+ return config;
244
+ });
245
+
246
+ return config;
247
+ };
248
+
249
+ module.exports = withPasskeyAutofill;
Binary file
@@ -0,0 +1,9 @@
1
+ export type ReactNativePasskeyAutofillModuleEvents = {
2
+ onPasskeyAdded: (event: {
3
+ success: boolean;
4
+ }) => void;
5
+ onPasskeyAuthenticated: (event: {
6
+ success: boolean;
7
+ }) => void;
8
+ };
9
+ //# sourceMappingURL=ReactNativePasskeyAutofill.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactNativePasskeyAutofill.types.d.ts","sourceRoot":"","sources":["../src/ReactNativePasskeyAutofill.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,sCAAsC,GAAG;IACnD,cAAc,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACtD,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;CAC/D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ReactNativePasskeyAutofill.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactNativePasskeyAutofill.types.js","sourceRoot":"","sources":["../src/ReactNativePasskeyAutofill.types.ts"],"names":[],"mappings":"","sourcesContent":["export type ReactNativePasskeyAutofillModuleEvents = {\n onPasskeyAdded: (event: { success: boolean }) => void;\n onPasskeyAuthenticated: (event: { success: boolean }) => void;\n};\n"]}
@@ -0,0 +1,12 @@
1
+ import { NativeModule } from 'expo';
2
+ import { ReactNativePasskeyAutofillModuleEvents } from './ReactNativePasskeyAutofill.types';
3
+ declare class ReactNativePasskeyAutofillModule extends NativeModule<ReactNativePasskeyAutofillModuleEvents> {
4
+ setMasterKey(secret: string): Promise<void>;
5
+ setHdRootKeyId(id: string): Promise<void>;
6
+ getHdRootKeyId(): Promise<string | null>;
7
+ configureIntentActions(getPasskeyAction: string, createPasskeyAction: string): Promise<void>;
8
+ clearCredentials(): Promise<void>;
9
+ }
10
+ declare const _default: ReactNativePasskeyAutofillModule;
11
+ export default _default;
12
+ //# sourceMappingURL=ReactNativePasskeyAutofillModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactNativePasskeyAutofillModule.d.ts","sourceRoot":"","sources":["../src/ReactNativePasskeyAutofillModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EAAE,sCAAsC,EAAE,MAAM,oCAAoC,CAAC;AAE5F,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;CAClC;;AAGD,wBAAmG"}
@@ -0,0 +1,4 @@
1
+ import { requireNativeModule } from 'expo';
2
+ // This call loads the native module object from the JSI.
3
+ export default requireNativeModule('ReactNativePasskeyAutofill');
4
+ //# sourceMappingURL=ReactNativePasskeyAutofillModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactNativePasskeyAutofillModule.js","sourceRoot":"","sources":["../src/ReactNativePasskeyAutofillModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAYzD,yDAAyD;AACzD,eAAe,mBAAmB,CAAmC,4BAA4B,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\nimport { ReactNativePasskeyAutofillModuleEvents } 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}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<ReactNativePasskeyAutofillModule>('ReactNativePasskeyAutofill');\n"]}
@@ -0,0 +1,3 @@
1
+ export { default } from './ReactNativePasskeyAutofillModule';
2
+ export * from './ReactNativePasskeyAutofill.types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAC7D,cAAe,oCAAoC,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // Reexport the native module. On web, it will be resolved to ReactNativePasskeyAutofillModule.web.ts
2
+ // and on native platforms to ReactNativePasskeyAutofillModule.ts
3
+ export { default } from './ReactNativePasskeyAutofillModule';
4
+ export * from './ReactNativePasskeyAutofill.types';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,iEAAiE;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAC7D,cAAe,oCAAoC,CAAC","sourcesContent":["// Reexport the native module. On web, it will be resolved to ReactNativePasskeyAutofillModule.web.ts\n// and on native platforms to ReactNativePasskeyAutofillModule.ts\nexport { default } from './ReactNativePasskeyAutofillModule';\nexport * from './ReactNativePasskeyAutofill.types';\n"]}
@@ -0,0 +1,10 @@
1
+ {
2
+ "platforms": ["apple", "android", "web"],
3
+ "apple": {
4
+ "modules": ["ReactNativePasskeyAutofillModule"]
5
+ },
6
+ "android": {
7
+ "modules": ["co.algorand.passkeyautofill.ReactNativePasskeyAutofillModule"]
8
+ },
9
+ "plugin": "./app.plugin.js"
10
+ }
@@ -0,0 +1,29 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'ReactNativePasskeyAutofill'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.description = package['description']
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.homepage = package['homepage']
13
+ s.platforms = {
14
+ :ios => '15.1',
15
+ :tvos => '15.1'
16
+ }
17
+ s.swift_version = '5.9'
18
+ s.source = { git: 'https://github.com/algorandfoundation/react-native-passkey-autofill' }
19
+ s.static_framework = true
20
+
21
+ s.dependency 'ExpoModulesCore'
22
+
23
+ # Swift/Objective-C compatibility
24
+ s.pod_target_xcconfig = {
25
+ 'DEFINES_MODULE' => 'YES',
26
+ }
27
+
28
+ s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
29
+ end