@algorandfoundation/react-native-passkey-autofill 1.0.0-canary.1 → 1.0.0-canary.3
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/README.md
CHANGED
|
@@ -16,6 +16,29 @@ npm install react-native-passkey-autofill
|
|
|
16
16
|
|
|
17
17
|
To use this module on Android, you need to configure the AutoFill service in your `AndroidManifest.xml` or via the Expo plugin.
|
|
18
18
|
|
|
19
|
+
#### Expo Plugin Configuration
|
|
20
|
+
|
|
21
|
+
If you are using Expo, you can configure the plugin in your `app.json` or `app.config.js`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"expo": {
|
|
26
|
+
"plugins": [
|
|
27
|
+
[
|
|
28
|
+
"react-native-passkey-autofill",
|
|
29
|
+
{
|
|
30
|
+
"site": "https://your-fido-server.com",
|
|
31
|
+
"label": "My Custom Credential Provider"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- `site`: The URL of your FIDO server (default: `https://fido.shore-tech.net`).
|
|
40
|
+
- `label`: The name of the credential provider as it appears in Android settings (default: `My Credential Provider`).
|
|
41
|
+
|
|
19
42
|
## Usage
|
|
20
43
|
|
|
21
44
|
```typescript
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
android:name=".service.PasskeyAutofillCredentialProviderService"
|
|
6
6
|
android:enabled="true"
|
|
7
7
|
android:exported="true"
|
|
8
|
-
android:label="
|
|
8
|
+
android:label="@string/passkey_autofill_label"
|
|
9
9
|
android:icon="@mipmap/ic_launcher"
|
|
10
10
|
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
|
|
11
11
|
tools:targetApi="upside_down_cake">
|
|
@@ -21,6 +21,7 @@ import androidx.credentials.webauthn.PublicKeyCredentialRequestOptions
|
|
|
21
21
|
import co.algorand.passkeyautofill.credentials.CredentialRepository
|
|
22
22
|
import co.algorand.passkeyautofill.credentials.Credential
|
|
23
23
|
import java.security.KeyPair
|
|
24
|
+
import java.security.MessageDigest
|
|
24
25
|
import android.util.Base64 as AndroidBase64
|
|
25
26
|
import org.json.JSONObject
|
|
26
27
|
|
|
@@ -39,10 +40,10 @@ class GetPasskeyActivity : AppCompatActivity() {
|
|
|
39
40
|
|
|
40
41
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
41
42
|
super.onCreate(savedInstanceState)
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
|
|
44
45
|
val credentialData = intent.getBundleExtra("CREDENTIAL_DATA")
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
if (request != null && credentialData != null) {
|
|
47
48
|
origin = credentialRepository.getOrigin(request!!.callingAppInfo)
|
|
48
49
|
credentialIdEnc = credentialData.getString("credentialId")
|
|
@@ -54,7 +55,7 @@ class GetPasskeyActivity : AppCompatActivity() {
|
|
|
54
55
|
orientation = android.widget.LinearLayout.VERTICAL
|
|
55
56
|
setPadding(50, 50, 50, 50)
|
|
56
57
|
}
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
val title = TextView(this).apply {
|
|
59
60
|
text = "Use Passkey"
|
|
60
61
|
textSize = 24f
|
|
@@ -67,7 +68,7 @@ class GetPasskeyActivity : AppCompatActivity() {
|
|
|
67
68
|
setPadding(0, 0, 0, 50)
|
|
68
69
|
}
|
|
69
70
|
layout.addView(info)
|
|
70
|
-
|
|
71
|
+
|
|
71
72
|
val confirmButton = Button(this).apply {
|
|
72
73
|
text = "Sign In"
|
|
73
74
|
setOnClickListener {
|
|
@@ -85,11 +86,18 @@ class GetPasskeyActivity : AppCompatActivity() {
|
|
|
85
86
|
val req = request ?: throw IllegalStateException("No request found")
|
|
86
87
|
val option = req.credentialOptions[0] as GetPublicKeyCredentialOption
|
|
87
88
|
val requestOptions = PublicKeyCredentialRequestOptions(option.requestJson)
|
|
88
|
-
|
|
89
|
+
|
|
89
90
|
val credId = AndroidBase64.decode(credentialIdEnc!!, AndroidBase64.DEFAULT)
|
|
91
|
+
|
|
92
|
+
val requestJson = JSONObject(option.requestJson)
|
|
93
|
+
val challenge = requestJson.getString("challenge")
|
|
94
|
+
val sanitizedOrigin = origin.replace(Regex("/$"), "")
|
|
95
|
+
|
|
90
96
|
val clientDataHash = option.requestData.getByteArray("androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH")
|
|
91
|
-
?:
|
|
92
|
-
|
|
97
|
+
?: MessageDigest.getInstance("SHA-256").digest(
|
|
98
|
+
"{\"type\":\"webauthn.get\",\"challenge\":\"$challenge\",\"origin\":\"$sanitizedOrigin\",\"crossOrigin\":false}".toByteArray(Charsets.UTF_8)
|
|
99
|
+
)
|
|
100
|
+
|
|
93
101
|
val dbCred = credentialRepository.getCredential(this, credId)
|
|
94
102
|
?: throw IllegalStateException("Credential not found")
|
|
95
103
|
|
|
@@ -118,10 +126,8 @@ class GetPasskeyActivity : AppCompatActivity() {
|
|
|
118
126
|
)
|
|
119
127
|
|
|
120
128
|
// Manual addition of clientDataJSON if needed by some RPs, following reference
|
|
121
|
-
val requestJson = JSONObject(option.requestJson)
|
|
122
|
-
val challenge = requestJson.getString("challenge")
|
|
123
129
|
val clientDataJSONb64 = getClientDataJSONb64(origin, challenge)
|
|
124
|
-
|
|
130
|
+
|
|
125
131
|
val delimiter = "response\":{"
|
|
126
132
|
val credentialJson = fidoCredential.json().substringBeforeLast(delimiter) + delimiter +
|
|
127
133
|
"\"clientDataJSON\":\"$clientDataJSONb64\"," +
|
|
@@ -129,12 +135,12 @@ class GetPasskeyActivity : AppCompatActivity() {
|
|
|
129
135
|
|
|
130
136
|
val resultIntent = Intent()
|
|
131
137
|
val passkeyCredential = PublicKeyCredential(credentialJson)
|
|
132
|
-
|
|
138
|
+
|
|
133
139
|
PendingIntentHandler.setGetCredentialResponse(
|
|
134
140
|
resultIntent,
|
|
135
141
|
GetCredentialResponse(passkeyCredential)
|
|
136
142
|
)
|
|
137
|
-
|
|
143
|
+
|
|
138
144
|
setResult(Activity.RESULT_OK, resultIntent)
|
|
139
145
|
} catch (e: Exception) {
|
|
140
146
|
Log.e(TAG, "Error during passkey assertion", e)
|
package/app.plugin.js
CHANGED
|
@@ -166,6 +166,7 @@ const withUserAgent = (config) => {
|
|
|
166
166
|
|
|
167
167
|
const withPasskeyAutofill = (config, props = {}) => {
|
|
168
168
|
const site = props.site || "https://fido.shore-tech.net";
|
|
169
|
+
const label = props.label || "My Credential Provider";
|
|
169
170
|
|
|
170
171
|
config = withAndroidCookieModule(config);
|
|
171
172
|
config = withUserAgent(config);
|
|
@@ -215,6 +216,10 @@ const withPasskeyAutofill = (config, props = {}) => {
|
|
|
215
216
|
},
|
|
216
217
|
]),
|
|
217
218
|
},
|
|
219
|
+
{
|
|
220
|
+
$: { name: "passkey_autofill_label", translatable: "true" },
|
|
221
|
+
_: label,
|
|
222
|
+
},
|
|
218
223
|
],
|
|
219
224
|
config.modResults
|
|
220
225
|
);
|