@capgo/capacitor-passkey 8.3.10 → 8.5.1
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 +6 -5
- package/android/src/main/java/app/capgo/passkey/CapacitorPasskeyPlugin.java +5 -1
- package/dist/docs.json +7 -0
- package/dist/esm/definitions.d.ts +60 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/internal.d.ts +1 -0
- package/dist/esm/internal.js.map +1 -1
- package/dist/esm/shim.js +2 -0
- package/dist/esm/shim.js.map +1 -1
- package/dist/esm/webauthn.js +1 -0
- package/dist/esm/webauthn.js.map +1 -1
- package/dist/plugin.cjs.js +3 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +3 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorPasskeyPlugin/CapacitorPasskey.swift +32 -0
- package/ios/Sources/CapacitorPasskeyPlugin/CapacitorPasskeyPlugin.swift +40 -16
- package/ios/Tests/CapacitorPasskeyPluginTests/CapacitorPasskeyPluginTests.swift +53 -0
- package/package.json +1 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import AuthenticationServices
|
|
2
|
+
import Capacitor
|
|
1
3
|
import Foundation
|
|
2
4
|
|
|
3
5
|
@objc public class CapacitorPasskey: NSObject {
|
|
@@ -31,6 +33,36 @@ import Foundation
|
|
|
31
33
|
|
|
32
34
|
return data
|
|
33
35
|
}
|
|
36
|
+
|
|
37
|
+
static func errorName(for code: ASAuthorizationError.Code) -> String {
|
|
38
|
+
switch code {
|
|
39
|
+
case .canceled, .failed, .notHandled:
|
|
40
|
+
return "NotAllowedError"
|
|
41
|
+
case .matchedExcludedCredential:
|
|
42
|
+
return "InvalidStateError"
|
|
43
|
+
default:
|
|
44
|
+
return "UnknownError"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static func nativeErrorDetails(from error: Error) -> JSObject? {
|
|
49
|
+
guard let authorizationError = error as? ASAuthorizationError else {
|
|
50
|
+
return nil
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let nsError = error as NSError
|
|
54
|
+
var details: JSObject = [
|
|
55
|
+
"platform": "ios",
|
|
56
|
+
"code": authorizationError.code.rawValue,
|
|
57
|
+
"domain": nsError.domain
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
if let reason = nsError.userInfo[NSLocalizedFailureReasonErrorKey] as? String {
|
|
61
|
+
details["failureReason"] = reason
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return details
|
|
65
|
+
}
|
|
34
66
|
}
|
|
35
67
|
|
|
36
68
|
struct PasskeyPluginError: Error {
|
|
@@ -119,9 +119,9 @@ private func normalizeDomain(_ value: String?) -> String? {
|
|
|
119
119
|
|
|
120
120
|
@objc(CapacitorPasskeyPlugin)
|
|
121
121
|
public class CapacitorPasskeyPlugin: CAPPlugin,
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
CAPBridgedPlugin,
|
|
123
|
+
ASAuthorizationControllerDelegate,
|
|
124
|
+
ASAuthorizationControllerPresentationContextProviding {
|
|
125
125
|
public let identifier = "CapacitorPasskeyPlugin"
|
|
126
126
|
public let jsName = "CapacitorPasskey"
|
|
127
127
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -166,7 +166,12 @@ public class CapacitorPasskeyPlugin: CAPPlugin,
|
|
|
166
166
|
let assertionRequest = try JSONDecoder().decode(PasskeyAssertionRequestJSON.self, from: requestJson)
|
|
167
167
|
let origin = normalizedOrigin(call.getString("origin"), rpId: assertionRequest.rpId)
|
|
168
168
|
let authorizationRequest = try buildAssertionRequest(from: assertionRequest, origin: origin)
|
|
169
|
-
performAuthorization(
|
|
169
|
+
performAuthorization(
|
|
170
|
+
call: call,
|
|
171
|
+
request: authorizationRequest,
|
|
172
|
+
operation: .assertion,
|
|
173
|
+
preferImmediatelyAvailableCredentials: call.getBool("preferImmediatelyAvailableCredentials") ?? false
|
|
174
|
+
)
|
|
170
175
|
} catch let error as PasskeyPluginError {
|
|
171
176
|
reject(call, error: error)
|
|
172
177
|
} catch {
|
|
@@ -237,14 +242,17 @@ public class CapacitorPasskeyPlugin: CAPPlugin,
|
|
|
237
242
|
|
|
238
243
|
clearPendingRequest()
|
|
239
244
|
if let authorizationError = error as? ASAuthorizationError {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
case .matchedExcludedCredential:
|
|
244
|
-
reject(call, name: "InvalidStateError", message: authorizationError.localizedDescription, error: error)
|
|
245
|
-
default:
|
|
246
|
-
reject(call, name: "UnknownError", message: authorizationError.localizedDescription, error: error)
|
|
245
|
+
var data: JSObject = [:]
|
|
246
|
+
if let native = CapacitorPasskey.nativeErrorDetails(from: error) {
|
|
247
|
+
data["native"] = native
|
|
247
248
|
}
|
|
249
|
+
reject(
|
|
250
|
+
call,
|
|
251
|
+
name: CapacitorPasskey.errorName(for: authorizationError.code),
|
|
252
|
+
message: authorizationError.localizedDescription,
|
|
253
|
+
error: error,
|
|
254
|
+
data: data
|
|
255
|
+
)
|
|
248
256
|
return
|
|
249
257
|
}
|
|
250
258
|
|
|
@@ -255,7 +263,12 @@ public class CapacitorPasskeyPlugin: CAPPlugin,
|
|
|
255
263
|
bridge?.viewController?.view.window ?? ASPresentationAnchor()
|
|
256
264
|
}
|
|
257
265
|
|
|
258
|
-
private func performAuthorization(
|
|
266
|
+
private func performAuthorization(
|
|
267
|
+
call: CAPPluginCall,
|
|
268
|
+
request: ASAuthorizationRequest,
|
|
269
|
+
operation: PasskeyOperation,
|
|
270
|
+
preferImmediatelyAvailableCredentials: Bool = false
|
|
271
|
+
) {
|
|
259
272
|
pendingCall = call
|
|
260
273
|
pendingOperation = operation
|
|
261
274
|
|
|
@@ -263,7 +276,11 @@ public class CapacitorPasskeyPlugin: CAPPlugin,
|
|
|
263
276
|
let controller = ASAuthorizationController(authorizationRequests: [request])
|
|
264
277
|
controller.delegate = self
|
|
265
278
|
controller.presentationContextProvider = self
|
|
266
|
-
|
|
279
|
+
if #available(iOS 16.0, *), preferImmediatelyAvailableCredentials {
|
|
280
|
+
controller.performRequests(options: .preferImmediatelyAvailableCredentials)
|
|
281
|
+
} else {
|
|
282
|
+
controller.performRequests()
|
|
283
|
+
}
|
|
267
284
|
}
|
|
268
285
|
}
|
|
269
286
|
|
|
@@ -381,8 +398,15 @@ public class CapacitorPasskeyPlugin: CAPPlugin,
|
|
|
381
398
|
reject(call, name: error.name, message: error.message)
|
|
382
399
|
}
|
|
383
400
|
|
|
384
|
-
private func reject(
|
|
385
|
-
|
|
386
|
-
|
|
401
|
+
private func reject(
|
|
402
|
+
_ call: CAPPluginCall,
|
|
403
|
+
name: String,
|
|
404
|
+
message: String,
|
|
405
|
+
error: Error? = nil,
|
|
406
|
+
data: JSObject = [:]
|
|
407
|
+
) {
|
|
408
|
+
var payload = data
|
|
409
|
+
payload["name"] = name
|
|
410
|
+
call.reject(message, name, error, payload)
|
|
387
411
|
}
|
|
388
412
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import AuthenticationServices
|
|
1
2
|
import XCTest
|
|
2
3
|
@testable import CapacitorPasskeyPlugin
|
|
3
4
|
|
|
@@ -14,4 +15,56 @@ class CapacitorPasskeyPluginTests: XCTestCase {
|
|
|
14
15
|
|
|
15
16
|
XCTAssertEqual(input, decoded)
|
|
16
17
|
}
|
|
18
|
+
|
|
19
|
+
func testErrorNameMatrix() {
|
|
20
|
+
let cases: [(ASAuthorizationError.Code, String)] = [
|
|
21
|
+
(.canceled, "NotAllowedError"),
|
|
22
|
+
(.failed, "NotAllowedError"),
|
|
23
|
+
(.notHandled, "NotAllowedError"),
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
for (code, expected) in cases {
|
|
27
|
+
XCTAssertEqual(
|
|
28
|
+
CapacitorPasskey.errorName(for: code),
|
|
29
|
+
expected,
|
|
30
|
+
"ASAuthorizationError.Code rawValue \(code.rawValue)"
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if #available(iOS 18.0, *) {
|
|
35
|
+
XCTAssertEqual(
|
|
36
|
+
CapacitorPasskey.errorName(for: .matchedExcludedCredential),
|
|
37
|
+
"InvalidStateError"
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func testNativeErrorDetailsMatrix() throws {
|
|
43
|
+
let associationFailure = NSError(
|
|
44
|
+
domain: ASAuthorizationError.errorDomain,
|
|
45
|
+
code: ASAuthorizationError.Code.failed.rawValue,
|
|
46
|
+
userInfo: [NSLocalizedFailureReasonErrorKey: "Unable to verify webcredentials association"]
|
|
47
|
+
)
|
|
48
|
+
let associationDetails = try XCTUnwrap(CapacitorPasskey.nativeErrorDetails(from: associationFailure))
|
|
49
|
+
XCTAssertEqual(associationDetails["code"] as? Int, 1004)
|
|
50
|
+
XCTAssertEqual(associationDetails["platform"] as? String, "ios")
|
|
51
|
+
XCTAssertEqual(associationDetails["domain"] as? String, ASAuthorizationError.errorDomain)
|
|
52
|
+
XCTAssertEqual(
|
|
53
|
+
associationDetails["failureReason"] as? String,
|
|
54
|
+
"Unable to verify webcredentials association"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
let canceled = NSError(
|
|
58
|
+
domain: ASAuthorizationError.errorDomain,
|
|
59
|
+
code: ASAuthorizationError.Code.canceled.rawValue,
|
|
60
|
+
userInfo: [:]
|
|
61
|
+
)
|
|
62
|
+
let canceledDetails = try XCTUnwrap(CapacitorPasskey.nativeErrorDetails(from: canceled))
|
|
63
|
+
XCTAssertEqual(canceledDetails["code"] as? Int, 1001)
|
|
64
|
+
XCTAssertEqual(canceledDetails["platform"] as? String, "ios")
|
|
65
|
+
XCTAssertNil(canceledDetails["failureReason"])
|
|
66
|
+
|
|
67
|
+
let unrelated = NSError(domain: NSURLErrorDomain, code: NSURLErrorTimedOut, userInfo: nil)
|
|
68
|
+
XCTAssertNil(CapacitorPasskey.nativeErrorDetails(from: unrelated))
|
|
69
|
+
}
|
|
17
70
|
}
|
package/package.json
CHANGED