@capgo/capacitor-passkey 8.2.0

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 (37) hide show
  1. package/CapgoCapacitorPasskey.podspec +17 -0
  2. package/LICENSE +373 -0
  3. package/Package.swift +28 -0
  4. package/README.md +692 -0
  5. package/android/build.gradle +61 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/app/capgo/passkey/CapacitorPasskey.java +21 -0
  8. package/android/src/main/java/app/capgo/passkey/CapacitorPasskeyPlugin.java +299 -0
  9. package/android/src/main/res/.gitkeep +0 -0
  10. package/dist/docs.json +1034 -0
  11. package/dist/esm/definitions.d.ts +531 -0
  12. package/dist/esm/definitions.js +2 -0
  13. package/dist/esm/definitions.js.map +1 -0
  14. package/dist/esm/index.d.ts +4 -0
  15. package/dist/esm/index.js +87 -0
  16. package/dist/esm/index.js.map +1 -0
  17. package/dist/esm/internal.d.ts +14 -0
  18. package/dist/esm/internal.js +2 -0
  19. package/dist/esm/internal.js.map +1 -0
  20. package/dist/esm/shim.d.ts +8 -0
  21. package/dist/esm/shim.js +108 -0
  22. package/dist/esm/shim.js.map +1 -0
  23. package/dist/esm/web.d.ts +12 -0
  24. package/dist/esm/web.js +69 -0
  25. package/dist/esm/web.js.map +1 -0
  26. package/dist/esm/webauthn.d.ts +23 -0
  27. package/dist/esm/webauthn.js +440 -0
  28. package/dist/esm/webauthn.js.map +1 -0
  29. package/dist/plugin.cjs.js +707 -0
  30. package/dist/plugin.cjs.js.map +1 -0
  31. package/dist/plugin.js +710 -0
  32. package/dist/plugin.js.map +1 -0
  33. package/ios/Sources/CapacitorPasskeyPlugin/CapacitorPasskey.swift +39 -0
  34. package/ios/Sources/CapacitorPasskeyPlugin/CapacitorPasskeyPlugin.swift +388 -0
  35. package/ios/Tests/CapacitorPasskeyPluginTests/CapacitorPasskeyPluginTests.swift +17 -0
  36. package/package.json +105 -0
  37. package/scripts/configure-native.js +263 -0
@@ -0,0 +1,61 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ }
7
+
8
+ buildscript {
9
+ repositories {
10
+ google()
11
+ mavenCentral()
12
+ }
13
+ dependencies {
14
+ classpath 'com.android.tools.build:gradle:8.13.0'
15
+ }
16
+ }
17
+
18
+ apply plugin: 'com.android.library'
19
+
20
+ android {
21
+ namespace = "app.capgo.passkey"
22
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
23
+ defaultConfig {
24
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
25
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
26
+ versionCode 1
27
+ versionName "1.0"
28
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
29
+ }
30
+ buildTypes {
31
+ release {
32
+ minifyEnabled false
33
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
34
+ }
35
+ }
36
+ lintOptions {
37
+ abortOnError = false
38
+ }
39
+ compileOptions {
40
+ sourceCompatibility JavaVersion.VERSION_21
41
+ targetCompatibility JavaVersion.VERSION_21
42
+ }
43
+ }
44
+
45
+ repositories {
46
+ google()
47
+ mavenCentral()
48
+ }
49
+
50
+
51
+ dependencies {
52
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
53
+ implementation project(':capacitor-android')
54
+ annotationProcessor project(':capacitor-android')
55
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
56
+ implementation "androidx.credentials:credentials:1.5.0"
57
+ implementation "androidx.credentials:credentials-play-services-auth:1.5.0"
58
+ testImplementation "junit:junit:$junitVersion"
59
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
60
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
61
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,21 @@
1
+ package app.capgo.passkey;
2
+
3
+ import androidx.credentials.webauthn.PublicKeyCredentialCreationOptions;
4
+ import androidx.credentials.webauthn.PublicKeyCredentialRequestOptions;
5
+
6
+ public class CapacitorPasskey {
7
+
8
+ public static final String PLUGIN_VERSION = "8.0.0";
9
+
10
+ public String getPluginVersion() {
11
+ return PLUGIN_VERSION;
12
+ }
13
+
14
+ public void validateCreationRequestJson(String requestJson) {
15
+ new PublicKeyCredentialCreationOptions(requestJson);
16
+ }
17
+
18
+ public void validateAssertionRequestJson(String requestJson) {
19
+ new PublicKeyCredentialRequestOptions(requestJson);
20
+ }
21
+ }
@@ -0,0 +1,299 @@
1
+ package app.capgo.passkey;
2
+
3
+ import android.app.Activity;
4
+ import android.os.Build;
5
+ import androidx.core.content.ContextCompat;
6
+ import androidx.credentials.CreateCredentialResponse;
7
+ import androidx.credentials.CreatePublicKeyCredentialRequest;
8
+ import androidx.credentials.CreatePublicKeyCredentialResponse;
9
+ import androidx.credentials.Credential;
10
+ import androidx.credentials.CredentialManager;
11
+ import androidx.credentials.CredentialManagerCallback;
12
+ import androidx.credentials.GetCredentialRequest;
13
+ import androidx.credentials.GetCredentialResponse;
14
+ import androidx.credentials.GetPublicKeyCredentialOption;
15
+ import androidx.credentials.PublicKeyCredential;
16
+ import androidx.credentials.exceptions.CreateCredentialException;
17
+ import androidx.credentials.exceptions.GetCredentialException;
18
+ import androidx.credentials.exceptions.NoCredentialException;
19
+ import androidx.credentials.exceptions.domerrors.DomError;
20
+ import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException;
21
+ import androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialDomException;
22
+ import com.getcapacitor.JSObject;
23
+ import com.getcapacitor.Plugin;
24
+ import com.getcapacitor.PluginCall;
25
+ import com.getcapacitor.PluginConfig;
26
+ import com.getcapacitor.PluginMethod;
27
+ import com.getcapacitor.annotation.CapacitorPlugin;
28
+ import java.net.URI;
29
+ import java.net.URISyntaxException;
30
+ import java.util.ArrayList;
31
+ import java.util.LinkedHashSet;
32
+ import java.util.List;
33
+ import org.json.JSONException;
34
+ import org.json.JSONObject;
35
+
36
+ @CapacitorPlugin(name = "CapacitorPasskey")
37
+ public class CapacitorPasskeyPlugin extends Plugin {
38
+
39
+ private final CapacitorPasskey implementation = new CapacitorPasskey();
40
+ private CredentialManager credentialManager;
41
+
42
+ @Override
43
+ public void load() {
44
+ super.load();
45
+ try {
46
+ credentialManager = CredentialManager.create(getContext());
47
+ } catch (Exception exception) {
48
+ credentialManager = null;
49
+ }
50
+ }
51
+
52
+ @PluginMethod
53
+ public void createCredential(final PluginCall call) {
54
+ if (!isCredentialManagerAvailable(call)) {
55
+ return;
56
+ }
57
+
58
+ final String requestJson = call.getString("requestJson");
59
+ if (requestJson == null || requestJson.isBlank()) {
60
+ rejectDom(call, "DataError", "requestJson is required.", null);
61
+ return;
62
+ }
63
+
64
+ try {
65
+ implementation.validateCreationRequestJson(requestJson);
66
+ } catch (Exception exception) {
67
+ rejectDom(call, "SyntaxError", "Invalid passkey registration request JSON.", exception);
68
+ return;
69
+ }
70
+
71
+ final CreatePublicKeyCredentialRequest request;
72
+ try {
73
+ request = new CreatePublicKeyCredentialRequest(requestJson);
74
+ } catch (Exception exception) {
75
+ rejectDom(call, "SyntaxError", "Could not create the native registration request.", exception);
76
+ return;
77
+ }
78
+
79
+ bridge.executeOnMainThread(() -> {
80
+ Activity activity = getActivity();
81
+ if (activity == null) {
82
+ rejectDom(call, "NotAllowedError", "Activity not available.", null);
83
+ return;
84
+ }
85
+
86
+ credentialManager.createCredentialAsync(
87
+ activity,
88
+ request,
89
+ null,
90
+ ContextCompat.getMainExecutor(getContext()),
91
+ new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
92
+ @Override
93
+ public void onResult(CreateCredentialResponse response) {
94
+ if (!(response instanceof CreatePublicKeyCredentialResponse)) {
95
+ rejectDom(call, "UnknownError", "Unexpected native registration response.", null);
96
+ return;
97
+ }
98
+
99
+ resolveJson(call, ((CreatePublicKeyCredentialResponse) response).getRegistrationResponseJson());
100
+ }
101
+
102
+ @Override
103
+ public void onError(CreateCredentialException exception) {
104
+ rejectCreateException(call, exception);
105
+ }
106
+ }
107
+ );
108
+ });
109
+ }
110
+
111
+ @PluginMethod
112
+ public void getCredential(final PluginCall call) {
113
+ if (!isCredentialManagerAvailable(call)) {
114
+ return;
115
+ }
116
+
117
+ final String requestJson = call.getString("requestJson");
118
+ if (requestJson == null || requestJson.isBlank()) {
119
+ rejectDom(call, "DataError", "requestJson is required.", null);
120
+ return;
121
+ }
122
+
123
+ try {
124
+ implementation.validateAssertionRequestJson(requestJson);
125
+ } catch (Exception exception) {
126
+ rejectDom(call, "SyntaxError", "Invalid passkey authentication request JSON.", exception);
127
+ return;
128
+ }
129
+
130
+ final GetCredentialRequest request;
131
+ try {
132
+ request = new GetCredentialRequest(List.of(new GetPublicKeyCredentialOption(requestJson)));
133
+ } catch (Exception exception) {
134
+ rejectDom(call, "SyntaxError", "Could not create the native authentication request.", exception);
135
+ return;
136
+ }
137
+
138
+ bridge.executeOnMainThread(() -> {
139
+ Activity activity = getActivity();
140
+ if (activity == null) {
141
+ rejectDom(call, "NotAllowedError", "Activity not available.", null);
142
+ return;
143
+ }
144
+
145
+ credentialManager.getCredentialAsync(
146
+ activity,
147
+ request,
148
+ null,
149
+ ContextCompat.getMainExecutor(getContext()),
150
+ new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
151
+ @Override
152
+ public void onResult(GetCredentialResponse response) {
153
+ Credential credential = response.getCredential();
154
+ if (!(credential instanceof PublicKeyCredential)) {
155
+ rejectDom(call, "UnknownError", "Unexpected native authentication response.", null);
156
+ return;
157
+ }
158
+
159
+ resolveJson(call, ((PublicKeyCredential) credential).getAuthenticationResponseJson());
160
+ }
161
+
162
+ @Override
163
+ public void onError(GetCredentialException exception) {
164
+ rejectGetException(call, exception);
165
+ }
166
+ }
167
+ );
168
+ });
169
+ }
170
+
171
+ @PluginMethod
172
+ public void getConfiguration(final PluginCall call) {
173
+ PluginConfig config = getConfig();
174
+ JSObject result = new JSObject();
175
+ String origin = normalizeOrigin(config.getString("origin"));
176
+ result.put("autoShim", config.getBoolean("autoShim", true));
177
+ result.put("domains", normalizeDomains(config.getArray("domains"), origin));
178
+ result.put("origin", origin);
179
+ result.put("platform", "android");
180
+ call.resolve(result);
181
+ }
182
+
183
+ @PluginMethod
184
+ public void isSupported(final PluginCall call) {
185
+ JSObject result = new JSObject();
186
+ result.put("available", credentialManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P);
187
+ result.put("conditionalMediation", false);
188
+ result.put("platform", "android");
189
+ call.resolve(result);
190
+ }
191
+
192
+ @PluginMethod
193
+ public void getPluginVersion(final PluginCall call) {
194
+ JSObject result = new JSObject();
195
+ result.put("version", implementation.getPluginVersion());
196
+ call.resolve(result);
197
+ }
198
+
199
+ private boolean isCredentialManagerAvailable(PluginCall call) {
200
+ if (credentialManager == null) {
201
+ rejectDom(call, "NotSupportedError", "Credential Manager is not available on this device.", null);
202
+ return false;
203
+ }
204
+
205
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
206
+ rejectDom(call, "NotSupportedError", "Passkeys require Android API level 28 or higher.", null);
207
+ return false;
208
+ }
209
+
210
+ return true;
211
+ }
212
+
213
+ private void resolveJson(PluginCall call, String json) {
214
+ try {
215
+ call.resolve(JSObject.fromJSONObject(new JSONObject(json)));
216
+ } catch (JSONException exception) {
217
+ rejectDom(call, "UnknownError", "Native passkey response was not valid JSON.", exception);
218
+ }
219
+ }
220
+
221
+ private void rejectCreateException(PluginCall call, CreateCredentialException exception) {
222
+ if (exception instanceof CreatePublicKeyCredentialDomException) {
223
+ DomError domError = ((CreatePublicKeyCredentialDomException) exception).getDomError();
224
+ rejectDom(call, domError.getClass().getSimpleName(), messageFor(exception, "Passkey registration failed."), exception);
225
+ return;
226
+ }
227
+
228
+ rejectDom(call, "UnknownError", messageFor(exception, "Passkey registration failed."), exception);
229
+ }
230
+
231
+ private void rejectGetException(PluginCall call, GetCredentialException exception) {
232
+ if (exception instanceof GetPublicKeyCredentialDomException) {
233
+ DomError domError = ((GetPublicKeyCredentialDomException) exception).getDomError();
234
+ rejectDom(call, domError.getClass().getSimpleName(), messageFor(exception, "Passkey authentication failed."), exception);
235
+ return;
236
+ }
237
+
238
+ if (exception instanceof NoCredentialException) {
239
+ rejectDom(call, "NotFoundError", "No matching passkey was found.", exception);
240
+ return;
241
+ }
242
+
243
+ rejectDom(call, "UnknownError", messageFor(exception, "Passkey authentication failed."), exception);
244
+ }
245
+
246
+ private String messageFor(CreateCredentialException exception, String fallback) {
247
+ CharSequence message = exception.getErrorMessage();
248
+ return message != null ? message.toString() : fallback;
249
+ }
250
+
251
+ private String messageFor(GetCredentialException exception, String fallback) {
252
+ CharSequence message = exception.getErrorMessage();
253
+ return message != null ? message.toString() : fallback;
254
+ }
255
+
256
+ private void rejectDom(PluginCall call, String name, String message, Exception exception) {
257
+ JSObject data = new JSObject();
258
+ data.put("name", name);
259
+ call.reject(message, name, exception, data);
260
+ }
261
+
262
+ private ArrayList<String> normalizeDomains(String[] configuredDomains, String origin) {
263
+ LinkedHashSet<String> domains = new LinkedHashSet<>();
264
+ if (configuredDomains != null) {
265
+ for (String value : configuredDomains) {
266
+ String normalized = normalizeDomain(value);
267
+ if (normalized != null) {
268
+ domains.add(normalized);
269
+ }
270
+ }
271
+ }
272
+
273
+ String originDomain = normalizeDomain(origin);
274
+ if (originDomain != null) {
275
+ domains.add(originDomain);
276
+ }
277
+
278
+ return new ArrayList<>(domains);
279
+ }
280
+
281
+ private String normalizeOrigin(String value) {
282
+ return value == null || value.isBlank() ? null : value;
283
+ }
284
+
285
+ private String normalizeDomain(String value) {
286
+ if (value == null || value.isBlank()) {
287
+ return null;
288
+ }
289
+
290
+ try {
291
+ URI uri = new URI(value);
292
+ if (uri.getHost() != null && !uri.getHost().isBlank()) {
293
+ return uri.getHost();
294
+ }
295
+ } catch (URISyntaxException ignored) {}
296
+
297
+ return value;
298
+ }
299
+ }
File without changes