Skip to content

Commit ed7fbf9

Browse files
authored
fix: Fixing service errors being reported as .unknown when sign in fails (#170)
* fix: Fixing service errors being reported as .unknown when sign in fails. Also adding proper WebAuthn cases to the AWSCognitoAuthError enum. * addressing PR comment
1 parent f55d2bb commit ed7fbf9

File tree

9 files changed

+93
-43
lines changed

9 files changed

+93
-43
lines changed

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/WebAuthn/AssertWebAuthnCredentials.swift

+14-11
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,27 @@ struct AssertWebAuthnCredentials: Action {
5252
))
5353
logVerbose("\(#fileID) Sending event \(event)", environment: environment)
5454
await dispatcher.send(event)
55-
} catch let error as WebAuthnError {
56-
logVerbose("\(#fileID) Raised error \(error)", environment: environment)
57-
let event = WebAuthnEvent(
58-
eventType: .error(error, respondToAuthChallenge)
59-
)
60-
await dispatcher.send(event)
6155
} catch {
6256
logVerbose("\(#fileID) Raised error \(error)", environment: environment)
63-
let webAuthnError = WebAuthnError.unknown(
64-
message: "Unable to assert WebAuthn credentials",
65-
error: error
66-
)
6757
let event = WebAuthnEvent(
68-
eventType: .error(webAuthnError, respondToAuthChallenge)
58+
eventType: .error(webAuthnError(from: error), respondToAuthChallenge)
6959
)
7060
await dispatcher.send(event)
7161
}
7262
}
63+
64+
private func webAuthnError(from error: Error) -> WebAuthnError {
65+
if let webAuthnError = error as? WebAuthnError {
66+
return webAuthnError
67+
}
68+
if let authError = error as? AuthErrorConvertible {
69+
return .service(error: authError.authError)
70+
}
71+
return .unknown(
72+
message: "Unable to assert WebAuthn credentials",
73+
error: error
74+
)
75+
}
7376
}
7477

7578
@available(iOS 17.4, macOS 13.5, *)

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/WebAuthn/FetchCredentialOptions.swift

+14-5
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,25 @@ struct FetchCredentialOptions: Action {
6767
await dispatcher.send(event)
6868
} catch {
6969
logVerbose("\(#fileID) Caught error \(error)", environment: environment)
70-
let webAuthnError = WebAuthnError.unknown(
71-
message: "Unable to fetch credential creation options",
72-
error: error
73-
)
7470
let event = WebAuthnEvent(
75-
eventType: .error(webAuthnError, respondToAuthChallenge)
71+
eventType: .error(webAuthnError(from: error), respondToAuthChallenge)
7672
)
7773
await dispatcher.send(event)
7874
}
7975
}
76+
77+
private func webAuthnError(from error: Error) -> WebAuthnError {
78+
if let webAuthnError = error as? WebAuthnError {
79+
return webAuthnError
80+
}
81+
if let authError = error as? AuthErrorConvertible {
82+
return .service(error: authError.authError)
83+
}
84+
return .unknown(
85+
message: "Unable to fetch credential creation options",
86+
error: error
87+
)
88+
}
8089
}
8190

8291
extension FetchCredentialOptions: DefaultLogger { }

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/WebAuthn/InitializeWebAuthn.swift

+14-5
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,25 @@ struct InitializeWebAuthn: Action {
5252
await dispatcher.send(event)
5353
} catch {
5454
logVerbose("\(#fileID) Caught error \(error)", environment: environment)
55-
let webAuthnError = WebAuthnError.unknown(
56-
message: "Unable to initiate WebAuthn flow",
57-
error: error
58-
)
5955
let event = WebAuthnEvent(
60-
eventType: .error(webAuthnError, respondToAuthChallenge)
56+
eventType: .error(webAuthnError(from: error), respondToAuthChallenge)
6157
)
6258
await dispatcher.send(event)
6359
}
6460
}
61+
62+
private func webAuthnError(from error: Error) -> WebAuthnError {
63+
if let webAuthnError = error as? WebAuthnError {
64+
return webAuthnError
65+
}
66+
if let authError = error as? AuthErrorConvertible {
67+
return .service(error: authError.authError)
68+
}
69+
return .unknown(
70+
message: "Unable to initiate WebAuthn flow",
71+
error: error
72+
)
73+
}
6574
}
6675

6776
extension InitializeWebAuthn: DefaultLogger { }

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/WebAuthn/VerifyWebAuthnCredential.swift

+14-5
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,25 @@ struct VerifyWebAuthnCredential: Action {
7070
await dispatcher.send(event)
7171
} catch {
7272
logVerbose("\(#fileID) Caught error \(error)", environment: environment)
73-
let webAuthnError = WebAuthnError.unknown(
74-
message: "Unable to verify WebAuthn credential",
75-
error: error
76-
)
7773
let event = WebAuthnEvent(
78-
eventType: .error(webAuthnError, respondToAuthChallenge)
74+
eventType: .error(webAuthnError(from: error), respondToAuthChallenge)
7975
)
8076
await dispatcher.send(event)
8177
}
8278
}
79+
80+
private func webAuthnError(from error: Error) -> WebAuthnError {
81+
if let webAuthnError = error as? WebAuthnError {
82+
return webAuthnError
83+
}
84+
if let authError = error as? AuthErrorConvertible {
85+
return .service(error: authError.authError)
86+
}
87+
return .unknown(
88+
message: "Unable to verify WebAuthn credential",
89+
error: error
90+
)
91+
}
8392
}
8493

8594
@available(iOS 17.4, macOS 13.5, *)

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/Errors/AWSCognitoAuthError.swift

+20-2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,24 @@ public enum AWSCognitoAuthError: Error {
8989
/// Thrown when a user tries to use a login which is already linked to another account.
9090
case resourceConflictException
9191

92-
/// WebAuthn related issue
93-
case webAuthn
92+
/// The WebAuthn credentials don't match an existing request
93+
case webAuthnChallengeNotFound
94+
95+
/// The client doesn't support WebAuhn authentication
96+
case webAuthnClientMismatch
97+
98+
/// WebAuthn is not supported on this device
99+
case webAuthnNotSupported
100+
101+
/// WebAuthn is not enabled
102+
case webAuthnNotEnabled
103+
104+
/// The device origin is not registered as an allowed origin
105+
case webAuthnOriginNotAllowed
106+
107+
/// The relying party ID doesn't match
108+
case webAuthnRelyingPartyMismatch
109+
110+
/// The WebAuthm configuration is missing or incomplete
111+
case webAuthnConfigurationMissing
94112
}

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/AWSCongnitoIdentityProvider+AuthErrorConvertible.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ extension AWSCognitoIdentityProvider.WebAuthnChallengeNotFoundException: AuthErr
345345
.service(
346346
message ?? fallbackDescription,
347347
AuthPluginErrorConstants.webAuthnChallengeNotFound,
348-
AWSCognitoAuthError.webAuthn
348+
AWSCognitoAuthError.webAuthnChallengeNotFound
349349
)
350350
}
351351
}
@@ -357,7 +357,7 @@ extension AWSCognitoIdentityProvider.WebAuthnClientMismatchException: AuthErrorC
357357
.service(
358358
message ?? fallbackDescription,
359359
AuthPluginErrorConstants.webAuthnClientMismatch,
360-
AWSCognitoAuthError.webAuthn
360+
AWSCognitoAuthError.webAuthnClientMismatch
361361
)
362362
}
363363
}
@@ -369,7 +369,7 @@ extension AWSCognitoIdentityProvider.WebAuthnCredentialNotSupportedException: Au
369369
.service(
370370
message ?? fallbackDescription,
371371
AuthPluginErrorConstants.webAuthnCredentialNotSupported,
372-
AWSCognitoAuthError.webAuthn
372+
AWSCognitoAuthError.webAuthnNotSupported
373373
)
374374
}
375375
}
@@ -381,7 +381,7 @@ extension AWSCognitoIdentityProvider.WebAuthnNotEnabledException: AuthErrorConve
381381
.service(
382382
message ?? fallbackDescription,
383383
AuthPluginErrorConstants.webAuthnNotEnabled,
384-
AWSCognitoAuthError.webAuthn
384+
AWSCognitoAuthError.webAuthnNotEnabled
385385
)
386386
}
387387
}
@@ -393,7 +393,7 @@ extension AWSCognitoIdentityProvider.WebAuthnOriginNotAllowedException: AuthErro
393393
.service(
394394
message ?? fallbackDescription,
395395
AuthPluginErrorConstants.webAuthnOriginNotAllowed,
396-
AWSCognitoAuthError.webAuthn
396+
AWSCognitoAuthError.webAuthnOriginNotAllowed
397397
)
398398
}
399399
}
@@ -405,7 +405,7 @@ extension AWSCognitoIdentityProvider.WebAuthnRelyingPartyMismatchException: Auth
405405
.service(
406406
message ?? fallbackDescription,
407407
AuthPluginErrorConstants.webAuthnRelyingPartyMismatch,
408-
AWSCognitoAuthError.webAuthn
408+
AWSCognitoAuthError.webAuthnRelyingPartyMismatch
409409
)
410410
}
411411
}
@@ -417,7 +417,7 @@ extension AWSCognitoIdentityProvider.WebAuthnConfigurationMissingException: Auth
417417
.service(
418418
message ?? fallbackDescription,
419419
AuthPluginErrorConstants.webAuthnConfigurationMissing,
420-
nil
420+
AWSCognitoAuthError.webAuthnConfigurationMissing
421421
)
422422
}
423423
}

AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/WebAuthnBehaviourTests/AssociateWebAuthnCredentialTaskTests.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class AssociateWebAuthnCredentialTaskTests: XCTestCase {
160160
return
161161
}
162162

163-
XCTAssertEqual(underlyingError as? AWSCognitoAuthError, AWSCognitoAuthError.webAuthn)
163+
XCTAssertEqual(underlyingError as? AWSCognitoAuthError, AWSCognitoAuthError.webAuthnNotEnabled)
164164
XCTAssertEqual(credentialRegistrant.createCallCount, 0)
165165
XCTAssertEqual(completeWebAuthnRegistrationCallCount, 0)
166166
} catch {
@@ -216,7 +216,7 @@ class AssociateWebAuthnCredentialTaskTests: XCTestCase {
216216
return
217217
}
218218

219-
XCTAssertEqual(underlyingError as? AWSCognitoAuthError, AWSCognitoAuthError.webAuthn)
219+
XCTAssertEqual(underlyingError as? AWSCognitoAuthError, AWSCognitoAuthError.webAuthnNotEnabled)
220220
XCTAssertEqual(startWebAuthnRegistrationCallCount, 1)
221221
XCTAssertEqual(credentialRegistrant.createCallCount, 1)
222222
} catch {

AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/WebAuthnBehaviourTests/DeleteWebAuthnCredentialTaskTests.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
@testable import AWSCognitoAuthPlugin
99
import enum Amplify.AuthError
1010
import enum AWSCognitoIdentity.CognitoIdentityClientTypes
11-
import struct AWSCognitoIdentityProvider.ForbiddenException
11+
import struct AWSCognitoIdentityProvider.WebAuthnClientMismatchException
1212
import XCTest
1313

1414
class DeleteWebAuthnCredentialTaskTests: XCTestCase {
@@ -83,17 +83,18 @@ class DeleteWebAuthnCredentialTaskTests: XCTestCase {
8383

8484
func testExecute_withServiceError_shouldFailWithServiceError() async {
8585
identityProvider.mockDeleteWebAuthnCredentialResponse = { _ in
86-
throw ForbiddenException(message: "Operation is forbidden")
86+
throw WebAuthnClientMismatchException(message: "Client mismatch")
8787
}
8888

8989
do {
9090
_ = try await task.execute()
9191
XCTFail("Task should have failed")
9292
} catch let error as AuthError {
93-
guard case .service = error else {
93+
guard case .service(_, _, let underlyingError) = error else {
9494
XCTFail("Expected AuthError.service error, got \(error)")
9595
return
9696
}
97+
XCTAssertEqual(underlyingError as? AWSCognitoAuthError, AWSCognitoAuthError.webAuthnClientMismatch)
9798
} catch {
9899
XCTFail("Expected AuthError error, got \(error)")
99100
}

AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/WebAuthnBehaviourTests/ListWebAuthnCredentialsTaskTests.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
@testable import AWSCognitoAuthPlugin
99
import enum Amplify.AuthError
1010
import enum AWSCognitoIdentity.CognitoIdentityClientTypes
11-
import struct AWSCognitoIdentityProvider.ForbiddenException
11+
import struct AWSCognitoIdentityProvider.WebAuthnRelyingPartyMismatchException
1212
import XCTest
1313

1414
class ListWebAuthnCredentialsTaskTests: XCTestCase {
@@ -109,17 +109,18 @@ class ListWebAuthnCredentialsTaskTests: XCTestCase {
109109

110110
func testExecute_withServiceError_shouldFailWithServiceError() async {
111111
identityProvider.mockListWebAuthnCredentialsResponse = { _ in
112-
throw ForbiddenException(message: "Operation is forbidden")
112+
throw WebAuthnRelyingPartyMismatchException(message: "Operation is forbidden")
113113
}
114114

115115
do {
116116
_ = try await task.execute()
117117
XCTFail("Task should have failed")
118118
} catch let error as AuthError {
119-
guard case .service = error else {
119+
guard case .service(_, _, let underlyingError) = error else {
120120
XCTFail("Expected AuthError.service error, got \(error)")
121121
return
122122
}
123+
XCTAssertEqual(underlyingError as? AWSCognitoAuthError, AWSCognitoAuthError.webAuthnRelyingPartyMismatch)
123124
} catch {
124125
XCTFail("Expected AuthError error, got \(error)")
125126
}

0 commit comments

Comments
 (0)