diff --git a/AGENTS.md b/AGENTS.md index 24df53e0..100113ae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -143,6 +143,39 @@ This SDK uses **Universal Login** for MFA step-up flows. When an MFA-required er See [Auth0.swift documentation](https://github.com/auth0/Auth0.swift) for detailed setup. +## MyAccount API Client — Required Pattern + +**All My Account API calls MUST use `MyAccountClientFactory.create()` instead of calling `Auth0.myAccount()` directly.** + +This factory attaches the `Auth0-Client` HTTP header that identifies requests as originating from the UI Components SDK. The library name and version are defined centrally in `Auth0UniversalComponents/Version.swift`: + +```swift +// Version.swift +let version = "1.0.0" +let libraryName = "universal-components-ios" +``` + +### Correct Usage + +```swift +// In any UseCase +let client = MyAccountClientFactory.create( + token: apiCredentials.accessToken, + domain: dependencies.domain, + session: session +) +let result = try await client.authenticationMethods.enrollTOTP().start() +``` + +### Incorrect Usage (will trigger SwiftLint error) + +```swift +// DO NOT use Auth0.myAccount() directly — this bypasses telemetry headers +let client = Auth0.myAccount(token: token, domain: domain, session: session) +``` + +--- + ## Key Technical Decisions ### UI Framework Support diff --git a/Auth0UniversalComponents/Auth0UniversalComponentsSDKInitializer.swift b/Auth0UniversalComponents/Auth0UniversalComponentsSDKInitializer.swift index 1a78be2c..573688f5 100644 --- a/Auth0UniversalComponents/Auth0UniversalComponentsSDKInitializer.swift +++ b/Auth0UniversalComponents/Auth0UniversalComponentsSDKInitializer.swift @@ -142,12 +142,6 @@ public actor Auth0UniversalComponentsSDKInitializer { tokenProvider: tokenProvider) } - /// Reset the SDK to an uninitialized state. - /// - /// This is primarily used for testing purposes to clear the singleton instance. - static func reset() { - _shared = nil - } } /// Ensures a URL string has an HTTPS scheme. diff --git a/Auth0UniversalComponents/AuthMethodsHome/Domain/GetAuthMethodsUseCase.swift b/Auth0UniversalComponents/AuthMethodsHome/Domain/GetAuthMethodsUseCase.swift index c11cc416..78550054 100644 --- a/Auth0UniversalComponents/AuthMethodsHome/Domain/GetAuthMethodsUseCase.swift +++ b/Auth0UniversalComponents/AuthMethodsHome/Domain/GetAuthMethodsUseCase.swift @@ -45,7 +45,7 @@ struct GetAuthMethodsUseCase: GetAuthMethodsUseCaseable { /// - Returns: Array of enrolled authentication methods /// - Throws: Auth0APIError if the request fails func execute(request: GetAuthMethodsRequest) async throws -> [AuthenticationMethod] { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .getAuthenticationMethods() .start() diff --git a/Auth0UniversalComponents/AuthMethodsHome/Domain/GetFactorsUseCase.swift b/Auth0UniversalComponents/AuthMethodsHome/Domain/GetFactorsUseCase.swift index d235b586..55ed2449 100644 --- a/Auth0UniversalComponents/AuthMethodsHome/Domain/GetFactorsUseCase.swift +++ b/Auth0UniversalComponents/AuthMethodsHome/Domain/GetFactorsUseCase.swift @@ -45,7 +45,7 @@ struct GetFactorsUseCase: GetFactorsUseCaseable { /// - Returns: Array of available authentication factors /// - Throws: Auth0APIError if the request fails func execute(request: GetFactorsRequest) async throws -> [Factor] { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .getFactors() .start() diff --git a/Auth0UniversalComponents/AuthMethodsHome/Presentation/MyAccountAuthMethodsViewModel.swift b/Auth0UniversalComponents/AuthMethodsHome/Presentation/MyAccountAuthMethodsViewModel.swift index e244cf77..7e805fdb 100644 --- a/Auth0UniversalComponents/AuthMethodsHome/Presentation/MyAccountAuthMethodsViewModel.swift +++ b/Auth0UniversalComponents/AuthMethodsHome/Presentation/MyAccountAuthMethodsViewModel.swift @@ -1,4 +1,5 @@ import Combine +import Foundation import SwiftUI import Auth0 diff --git a/Auth0UniversalComponents/Core/Telemetry/MyAccountClientFactory.swift b/Auth0UniversalComponents/Core/Telemetry/MyAccountClientFactory.swift new file mode 100644 index 00000000..189fd19c --- /dev/null +++ b/Auth0UniversalComponents/Core/Telemetry/MyAccountClientFactory.swift @@ -0,0 +1,21 @@ +import Auth0 +import Foundation + +/// Factory for creating MyAccount API clients with telemetry pre-applied. +/// +/// All UseCases should use this factory instead of calling `Auth0.myAccount()` directly. +/// This ensures the `Auth0-Client` header identifies requests as originating from +/// the UI Components SDK. +struct MyAccountClientFactory { + static func create(token: String, domain: String, session: URLSession = .shared) -> MyAccount { + var client = Auth0.myAccount(token: token, domain: domain, session: session) + client.using(inLibrary: libraryName, version: version) + return client + } + + static func create(token: String, domain: String) -> MyAccount { + var client = Auth0.myAccount(token: token, domain: domain) + client.using(inLibrary: libraryName, version: version) + return client + } +} diff --git a/Auth0UniversalComponents/Core/Utils/ErrorHandler.swift b/Auth0UniversalComponents/Core/Utils/ErrorHandler.swift index d8e0f0f2..b4e29fe6 100644 --- a/Auth0UniversalComponents/Core/Utils/ErrorHandler.swift +++ b/Auth0UniversalComponents/Core/Utils/ErrorHandler.swift @@ -197,4 +197,5 @@ struct ErrorHandler { retryCallback() } } + } diff --git a/Auth0UniversalComponents/EmailSMSEnrollment/Domain/ConfirmEmailEnrollmentUseCase.swift b/Auth0UniversalComponents/EmailSMSEnrollment/Domain/ConfirmEmailEnrollmentUseCase.swift index 89e0ef5e..0a00cc62 100644 --- a/Auth0UniversalComponents/EmailSMSEnrollment/Domain/ConfirmEmailEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/EmailSMSEnrollment/Domain/ConfirmEmailEnrollmentUseCase.swift @@ -61,7 +61,7 @@ struct ConfirmEmailEnrollmentUseCase: ConfirmEmailEnrollmentUseCaseable { /// - Returns: The newly created email authentication method /// - Throws: Auth0APIError if the OTP is invalid or the request fails func execute(request: ConfirmEmailEnrollmentRequest) async throws -> AuthenticationMethod { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .confirmEmailEnrollment(id: request.id, authSession: request.authSession, otpCode: request.otpCode) .start() diff --git a/Auth0UniversalComponents/EmailSMSEnrollment/Domain/ConfirmPhoneEnrollmentUseCase.swift b/Auth0UniversalComponents/EmailSMSEnrollment/Domain/ConfirmPhoneEnrollmentUseCase.swift index abcd4b12..c8a4b49a 100644 --- a/Auth0UniversalComponents/EmailSMSEnrollment/Domain/ConfirmPhoneEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/EmailSMSEnrollment/Domain/ConfirmPhoneEnrollmentUseCase.swift @@ -61,7 +61,7 @@ struct ConfirmPhoneEnrollmentUseCase: ConfirmPhoneEnrollmentUseCaseable { /// - Returns: The newly created phone authentication method /// - Throws: Auth0APIError if the OTP is invalid or the request fails func execute(request: ConfirmPhoneEnrollmentRequest) async throws -> AuthenticationMethod { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .confirmPhoneEnrollment(id: request.id, authSession: request.authSession, otpCode: request.otpCode) .start() diff --git a/Auth0UniversalComponents/EmailSMSEnrollment/Domain/StartEmailEnrollmentUseCase.swift b/Auth0UniversalComponents/EmailSMSEnrollment/Domain/StartEmailEnrollmentUseCase.swift index 4d604737..f9ad67b3 100644 --- a/Auth0UniversalComponents/EmailSMSEnrollment/Domain/StartEmailEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/EmailSMSEnrollment/Domain/StartEmailEnrollmentUseCase.swift @@ -53,7 +53,7 @@ struct StartEmailEnrollmentUseCase: StartEmailEnrollmentUseCaseable { /// - Returns: Email enrollment challenge with verification details /// - Throws: Auth0APIError if the request fails func execute(request: StartEmailEnrollmentRequest) async throws -> EmailEnrollmentChallenge { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .enrollEmail(emailAddress: request.email) .start() diff --git a/Auth0UniversalComponents/EmailSMSEnrollment/Domain/StartPhoneEnrollmentUseCase.swift b/Auth0UniversalComponents/EmailSMSEnrollment/Domain/StartPhoneEnrollmentUseCase.swift index 35eaf409..f723d70e 100644 --- a/Auth0UniversalComponents/EmailSMSEnrollment/Domain/StartPhoneEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/EmailSMSEnrollment/Domain/StartPhoneEnrollmentUseCase.swift @@ -56,7 +56,7 @@ struct StartPhoneEnrollmentUseCase: StartPhoneEnrollmentUseCaseable { /// - Returns: Phone enrollment challenge with verification details /// - Throws: Auth0APIError if the request fails func execute(request: StartPhoneEnrollmentRequest) async throws -> PhoneEnrollmentChallenge { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .enrollPhone(phoneNumber: request.phoneNumber, preferredAuthenticationMethod: request.preferredAuthenticationMethod) .start() diff --git a/Auth0UniversalComponents/Passkeys/Domain/ConfirmPasskeyEnrollmentUseCase.swift b/Auth0UniversalComponents/Passkeys/Domain/ConfirmPasskeyEnrollmentUseCase.swift index 7723d00d..781defe0 100644 --- a/Auth0UniversalComponents/Passkeys/Domain/ConfirmPasskeyEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/Passkeys/Domain/ConfirmPasskeyEnrollmentUseCase.swift @@ -52,7 +52,7 @@ struct ConfirmPasskeyEnrollmentUseCase: ConfirmPasskeyEnrollmentUseCaseable { /// - Returns: The enrolled passkey authentication method /// - Throws: Auth0APIError if the passkey creation or enrollment fails func execute(request: ConfirmPasskeyEnrollmentRequest) async throws -> PasskeyAuthenticationMethod { - try await Auth0.myAccount(token: request.token, domain: request.domain) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain) .authenticationMethods .enroll(passkey: request.passkey, challenge: request.challenge) .start() diff --git a/Auth0UniversalComponents/Passkeys/Domain/StartPasskeyEnrollmentUseCase.swift b/Auth0UniversalComponents/Passkeys/Domain/StartPasskeyEnrollmentUseCase.swift index ce0a0d17..09ea3cbc 100644 --- a/Auth0UniversalComponents/Passkeys/Domain/StartPasskeyEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/Passkeys/Domain/StartPasskeyEnrollmentUseCase.swift @@ -51,7 +51,7 @@ struct StartPasskeyEnrollmentUseCase: StartPasskeyEnrollmentUseCaseable { /// - Returns: Passkey enrollment challenge /// - Throws: Auth0APIError if the request fails func execute(request: StartPasskeyEnrollmentRequest) async throws -> PasskeyEnrollmentChallenge { - try await Auth0.myAccount(token: request.token, domain: request.domain) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain) .authenticationMethods .passkeyEnrollmentChallenge() .start() diff --git a/Auth0UniversalComponents/RecoveryCode/Domain/ConfirmRecoveryCodeEnrollmentUseCase.swift b/Auth0UniversalComponents/RecoveryCode/Domain/ConfirmRecoveryCodeEnrollmentUseCase.swift index 7d2c6aa9..ca103455 100644 --- a/Auth0UniversalComponents/RecoveryCode/Domain/ConfirmRecoveryCodeEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/RecoveryCode/Domain/ConfirmRecoveryCodeEnrollmentUseCase.swift @@ -59,7 +59,7 @@ struct ConfirmRecoveryCodeEnrollmentUseCase: ConfirmRecoveryCodeEnrollmentUseCas /// - Returns: The newly created recovery code authentication method /// - Throws: Auth0APIError if the request fails func execute(request: ConfirmRecoveryCodeEnrollmentRequest) async throws -> AuthenticationMethod { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .confirmRecoveryCodeEnrollment(id: request.id, authSession: request.authSession) .start() diff --git a/Auth0UniversalComponents/RecoveryCode/Domain/StartRecoveryCodeEnrollmentUseCase.swift b/Auth0UniversalComponents/RecoveryCode/Domain/StartRecoveryCodeEnrollmentUseCase.swift index 654e0a26..48a08b00 100644 --- a/Auth0UniversalComponents/RecoveryCode/Domain/StartRecoveryCodeEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/RecoveryCode/Domain/StartRecoveryCodeEnrollmentUseCase.swift @@ -46,7 +46,7 @@ struct StartRecoveryCodeEnrollmentUseCase: StartRecoveryCodeEnrollmentUseCaseabl /// - Returns: Recovery code enrollment challenge with generated codes /// - Throws: Auth0APIError if the request fails func execute(request: StartRecoveryCodeEnrollmentRequest) async throws -> RecoveryCodeEnrollmentChallenge { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .enrollRecoveryCode() .start() diff --git a/Auth0UniversalComponents/RecoveryCode/Presentation/RecoveryCodeEnrollmentViewModel.swift b/Auth0UniversalComponents/RecoveryCode/Presentation/RecoveryCodeEnrollmentViewModel.swift index 12a46578..a5fef0ce 100644 --- a/Auth0UniversalComponents/RecoveryCode/Presentation/RecoveryCodeEnrollmentViewModel.swift +++ b/Auth0UniversalComponents/RecoveryCode/Presentation/RecoveryCodeEnrollmentViewModel.swift @@ -1,4 +1,5 @@ import Combine +import Foundation import Auth0 /// View model for recovery code enrollment. diff --git a/Auth0UniversalComponents/SaveAuthenticators/Domain/DeleteAuthMethodUseCase.swift b/Auth0UniversalComponents/SaveAuthenticators/Domain/DeleteAuthMethodUseCase.swift index 068519ba..9c8b9b19 100644 --- a/Auth0UniversalComponents/SaveAuthenticators/Domain/DeleteAuthMethodUseCase.swift +++ b/Auth0UniversalComponents/SaveAuthenticators/Domain/DeleteAuthMethodUseCase.swift @@ -44,7 +44,7 @@ struct DeleteAuthMethodUseCase: DeleteAuthMethodUseCaseable { /// - Parameter request: Request containing the method ID to delete /// - Throws: Auth0APIError if the method cannot be deleted func execute(request: DeleteAuthMethodRequest) async throws { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .deleteAuthenticationMethod(by: request.id) .start() diff --git a/Auth0UniversalComponents/TOTPPushEnrollment/Domain/ConfirmPushEnrollmentUseCase.swift b/Auth0UniversalComponents/TOTPPushEnrollment/Domain/ConfirmPushEnrollmentUseCase.swift index 571cf24b..bfe8c35d 100644 --- a/Auth0UniversalComponents/TOTPPushEnrollment/Domain/ConfirmPushEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/TOTPPushEnrollment/Domain/ConfirmPushEnrollmentUseCase.swift @@ -58,7 +58,7 @@ struct ConfirmPushEnrollmentUseCase: ConfirmPushEnrollmentUseCaseable { /// - Returns: The newly created push notification authentication method /// - Throws: Auth0APIError if the request fails func execute(request: ConfirmPushEnrollmentRequest) async throws -> AuthenticationMethod { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .confirmPushNotificationEnrollment(id: request.id, authSession: request.authSession) .start() diff --git a/Auth0UniversalComponents/TOTPPushEnrollment/Domain/ConfirmTOTPEnrollmentUseCase.swift b/Auth0UniversalComponents/TOTPPushEnrollment/Domain/ConfirmTOTPEnrollmentUseCase.swift index 7835c40c..77c4abfb 100644 --- a/Auth0UniversalComponents/TOTPPushEnrollment/Domain/ConfirmTOTPEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/TOTPPushEnrollment/Domain/ConfirmTOTPEnrollmentUseCase.swift @@ -62,7 +62,7 @@ struct ConfirmTOTPEnrollmentUseCase: ConfirmTOTPEnrollmentUseCaseable { /// - Returns: The newly created TOTP authentication method /// - Throws: Auth0APIError if the OTP is invalid or the request fails func execute(request: ConfirmTOTPEnrollmentRequest) async throws -> AuthenticationMethod { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .confirmTOTPEnrollment(id: request.id, authSession: request.authSession, otpCode: request.otpCode) .start() diff --git a/Auth0UniversalComponents/TOTPPushEnrollment/Domain/StartPushEnrollmentUseCase.swift b/Auth0UniversalComponents/TOTPPushEnrollment/Domain/StartPushEnrollmentUseCase.swift index 9a654a2b..5e0b356f 100644 --- a/Auth0UniversalComponents/TOTPPushEnrollment/Domain/StartPushEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/TOTPPushEnrollment/Domain/StartPushEnrollmentUseCase.swift @@ -46,7 +46,7 @@ struct StartPushEnrollmentUseCase: StartPushEnrollmentUseCaseable { /// - Returns: Push notification enrollment challenge /// - Throws: Auth0APIError if the request fails func execute(request: StartPushEnrollmentRequest) async throws -> PushEnrollmentChallenge { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .enrollPushNotification() .start() diff --git a/Auth0UniversalComponents/TOTPPushEnrollment/Domain/StartTOTPEnrollmentUseCase.swift b/Auth0UniversalComponents/TOTPPushEnrollment/Domain/StartTOTPEnrollmentUseCase.swift index 33cb3d24..f97036c8 100644 --- a/Auth0UniversalComponents/TOTPPushEnrollment/Domain/StartTOTPEnrollmentUseCase.swift +++ b/Auth0UniversalComponents/TOTPPushEnrollment/Domain/StartTOTPEnrollmentUseCase.swift @@ -46,7 +46,7 @@ struct StartTOTPEnrollmentUseCase: StartTOTPEnrollmentUseCaseable { /// - Returns: TOTP enrollment challenge with QR code and secret /// - Throws: Auth0APIError if the request fails func execute(request: StartTOTPEnrollmentRequest) async throws -> TOTPEnrollmentChallenge { - try await Auth0.myAccount(token: request.token, domain: request.domain, session: session) + try await MyAccountClientFactory.create(token: request.token, domain: request.domain, session: session) .authenticationMethods .enrollTOTP() .start() diff --git a/Auth0UniversalComponents/Version.swift b/Auth0UniversalComponents/Version.swift index 4e1a35ed..d674d854 100644 --- a/Auth0UniversalComponents/Version.swift +++ b/Auth0UniversalComponents/Version.swift @@ -3,3 +3,7 @@ /// This version string follows semantic versioning and is used to track /// the SDK's release version. let version = "1.0.0" + +/// The library name used in the `Auth0-Client` HTTP header to identify +/// requests originating from the UI Components SDK. +let libraryName = "universal-components-ios" diff --git a/Auth0UniversalComponentsTests/Mocks/MockURLProtocol.swift b/Auth0UniversalComponentsTests/Mocks/MockURLProtocol.swift index 66beb548..102a7062 100644 --- a/Auth0UniversalComponentsTests/Mocks/MockURLProtocol.swift +++ b/Auth0UniversalComponentsTests/Mocks/MockURLProtocol.swift @@ -1,7 +1,14 @@ import Foundation final class MockURLProtocol: URLProtocol { - static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data?))? + nonisolated(unsafe) static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data?))? + + private var capturedHandler: ((URLRequest) throws -> (HTTPURLResponse, Data?))? + + override init(request: URLRequest, cachedResponse: CachedURLResponse?, client: (any URLProtocolClient)?) { + capturedHandler = Self.requestHandler + super.init(request: request, cachedResponse: cachedResponse, client: client) + } override static func canInit(with request: URLRequest) -> Bool { return true @@ -12,8 +19,9 @@ final class MockURLProtocol: URLProtocol { } override func startLoading() { - guard let handler = MockURLProtocol.requestHandler else { - fatalError("MockURLProtocol requires a requestHandler.") + guard let handler = capturedHandler else { + client?.urlProtocol(self, didFailWithError: URLError(.unknown)) + return } do { diff --git a/Auth0UniversalComponentsTests/Passkeys/PasskeysEnrollmentViewModelTests.swift b/Auth0UniversalComponentsTests/Passkeys/PasskeysEnrollmentViewModelTests.swift index 21f72d7a..5944d60f 100644 --- a/Auth0UniversalComponentsTests/Passkeys/PasskeysEnrollmentViewModelTests.swift +++ b/Auth0UniversalComponentsTests/Passkeys/PasskeysEnrollmentViewModelTests.swift @@ -85,7 +85,6 @@ struct PasskeysEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -110,7 +109,6 @@ struct PasskeysEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() let mockDelegate = MockRefreshDelegate() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -135,7 +133,6 @@ struct PasskeysEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -163,7 +160,6 @@ struct PasskeysEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -202,7 +198,6 @@ struct PasskeysEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -230,7 +225,6 @@ struct PasskeysEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -266,7 +260,6 @@ struct PasskeysEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, diff --git a/Auth0UniversalComponentsTests/RecoveryCode/RecoveryCodeEnrollmentViewModelTests.swift b/Auth0UniversalComponentsTests/RecoveryCode/RecoveryCodeEnrollmentViewModelTests.swift index b3059f84..8ca172fe 100644 --- a/Auth0UniversalComponentsTests/RecoveryCode/RecoveryCodeEnrollmentViewModelTests.swift +++ b/Auth0UniversalComponentsTests/RecoveryCode/RecoveryCodeEnrollmentViewModelTests.swift @@ -71,7 +71,6 @@ struct RecoveryCodeEnrollmentViewModelTests { @Test func testInit_initialState() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel(delegate: nil) @@ -85,7 +84,6 @@ struct RecoveryCodeEnrollmentViewModelTests { @Test func testLoadData() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel(startRecoveryCodeEnrollmentUseCase: StartRecoveryCodeEnrollmentUseCase(session: makeMockSession()), delegate: nil) @@ -108,7 +106,6 @@ struct RecoveryCodeEnrollmentViewModelTests { @Test func testConfirmEnrollment_success() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let startRecoveryCodeEnrollmentUseCase = StartRecoveryCodeEnrollmentUseCase(session: makeMockSession()) @@ -157,7 +154,6 @@ struct RecoveryCodeEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() let mockDelegate = MockRefreshDelegate() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel(delegate: mockDelegate) @@ -173,7 +169,6 @@ struct RecoveryCodeEnrollmentViewModelTests { let mockDelegate = MockRefreshDelegate() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let startRecoveryCodeEnrollmentUseCase = StartRecoveryCodeEnrollmentUseCase(session: makeMockSession()) @@ -222,7 +217,6 @@ struct RecoveryCodeEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel( @@ -261,7 +255,6 @@ struct RecoveryCodeEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel( @@ -287,7 +280,6 @@ struct RecoveryCodeEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let startRecoveryCodeEnrollmentUseCase = StartRecoveryCodeEnrollmentUseCase(session: makeMockSession()) @@ -343,7 +335,6 @@ struct RecoveryCodeEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel(delegate: nil) @@ -362,7 +353,6 @@ struct RecoveryCodeEnrollmentViewModelTests { @Test func testHandle_setsLoaderToFalse() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel(delegate: nil) @@ -388,7 +378,6 @@ struct RecoveryCodeEnrollmentViewModelTests { @Test func testHandle_webAuthError() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel(delegate: nil) @@ -410,7 +399,6 @@ struct RecoveryCodeEnrollmentViewModelTests { @Test func testHandle_myAccountError() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel(delegate: nil) @@ -433,7 +421,6 @@ struct RecoveryCodeEnrollmentViewModelTests { @Test func testHandle_credentialsManagerError() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await RecoveryCodeEnrollmentViewModel(delegate: nil) @@ -459,7 +446,6 @@ struct RecoveryCodeEnrollmentViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let startRecoveryCodeEnrollmentUseCase = StartRecoveryCodeEnrollmentUseCase(session: makeMockSession()) diff --git a/Auth0UniversalComponentsTests/SMSEmailEnrollment/EmailPhoneViewModelTests.swift b/Auth0UniversalComponentsTests/SMSEmailEnrollment/EmailPhoneViewModelTests.swift index f41ff96d..67cf4a24 100644 --- a/Auth0UniversalComponentsTests/SMSEmailEnrollment/EmailPhoneViewModelTests.swift +++ b/Auth0UniversalComponentsTests/SMSEmailEnrollment/EmailPhoneViewModelTests.swift @@ -52,7 +52,6 @@ struct EmailPhoneViewModelTests { @Test func testInit_initialState() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await EmailPhoneEnrollmentViewModel(type: .email) @@ -67,7 +66,6 @@ struct EmailPhoneViewModelTests { @Test func testStartEnrollment_Phone_Success() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await EmailPhoneEnrollmentViewModel(startPhoneEnrollmentUseCase: StartPhoneEnrollmentUseCase(session: makeMockSession()), @@ -92,7 +90,6 @@ struct EmailPhoneViewModelTests { @Test func testStartEnrollment_Email_Success() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await EmailPhoneEnrollmentViewModel(startEmailEnrollmentUseCase: StartEmailEnrollmentUseCase(session: makeMockSession()), @@ -116,7 +113,6 @@ struct EmailPhoneViewModelTests { @Test func navigationTitle() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await EmailPhoneEnrollmentViewModel(type: .sms) @@ -127,7 +123,6 @@ struct EmailPhoneViewModelTests { @Test func title() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await EmailPhoneEnrollmentViewModel(type: .sms) diff --git a/Auth0UniversalComponentsTests/SavedAuthenticators/MyAccountAuthMethodsViewModelTests.swift b/Auth0UniversalComponentsTests/SavedAuthenticators/MyAccountAuthMethodsViewModelTests.swift index ce6f1c2d..06dde10e 100644 --- a/Auth0UniversalComponentsTests/SavedAuthenticators/MyAccountAuthMethodsViewModelTests.swift +++ b/Auth0UniversalComponentsTests/SavedAuthenticators/MyAccountAuthMethodsViewModelTests.swift @@ -136,7 +136,6 @@ struct MyAccountAuthMethodsViewModelTests { @Test func testInitialState() async { - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -155,7 +154,6 @@ struct MyAccountAuthMethodsViewModelTests { @Test func testFetchingOfAuthMethodsSuccess() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, diff --git a/Auth0UniversalComponentsTests/SavedAuthenticators/SavedAuthenticatorsViewModelTests.swift b/Auth0UniversalComponentsTests/SavedAuthenticators/SavedAuthenticatorsViewModelTests.swift index 38c2cc9a..c7820a6c 100644 --- a/Auth0UniversalComponentsTests/SavedAuthenticators/SavedAuthenticatorsViewModelTests.swift +++ b/Auth0UniversalComponentsTests/SavedAuthenticators/SavedAuthenticatorsViewModelTests.swift @@ -83,7 +83,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testInit_initialState() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await SavedAuthenticatorsViewModel(type: .email, authenticationMethods: [], delegate: nil) @@ -95,7 +94,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testLoadDataSuccess() async throws { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await SavedAuthenticatorsViewModel(getAuthMethodsUseCase: GetAuthMethodsUseCase(session: makeMockSession()), type: .sms, authenticationMethods: [], delegate: nil) @@ -118,7 +116,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testDeletionSuccess() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let getAuthMethodsUseCase = GetAuthMethodsUseCase(session: makeMockSession()) @@ -162,7 +159,6 @@ struct SavedAuthenticatorsViewModelTests { let mockTokenProvider = MockTokenProvider() let mockDelegate = MockRefreshDelegate() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await SavedAuthenticatorsViewModel(type: .email, authenticationMethods: [], delegate: mockDelegate) @@ -175,7 +171,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testDeletion_callsDelegate() async { let mockTokenProvider = MockTokenProvider() let mockDelegate = MockRefreshDelegate() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let getAuthMethodsUseCase = GetAuthMethodsUseCase(session: makeMockSession()) @@ -217,7 +212,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testLoadData_handlesAPIError() async throws { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await SavedAuthenticatorsViewModel( @@ -256,7 +250,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testLoadData_handlesNetworkError() async throws { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await SavedAuthenticatorsViewModel( @@ -282,7 +275,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testDeletion_handlesAPIError() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let getAuthMethodsUseCase = GetAuthMethodsUseCase(session: makeMockSession()) @@ -334,7 +326,6 @@ struct SavedAuthenticatorsViewModelTests { /// of the one the user actually tapped. @Test func testDeletion_deletesOnlyTargetedAuthMethod() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) // Two distinct SMS methods loaded into the list. @@ -431,7 +422,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testLoadData_withPreloadedMethods() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await SavedAuthenticatorsViewModel( @@ -448,7 +438,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testLoadData_emptyResponse() async throws { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await SavedAuthenticatorsViewModel( @@ -485,7 +474,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testViewAuthenticationMethods_initialization() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let smsViewModel = await SavedAuthenticatorsViewModel(type: .sms, authenticationMethods: [], delegate: nil) @@ -501,7 +489,6 @@ struct SavedAuthenticatorsViewModelTests { @Test func testLoadData_populatesAuthenticationMethods() async throws { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await SavedAuthenticatorsViewModel( diff --git a/Auth0UniversalComponentsTests/TOTPPushEnrollment/OTPViewModelTests.swift b/Auth0UniversalComponentsTests/TOTPPushEnrollment/OTPViewModelTests.swift index b66d8166..bb0b0fdc 100644 --- a/Auth0UniversalComponentsTests/TOTPPushEnrollment/OTPViewModelTests.swift +++ b/Auth0UniversalComponentsTests/TOTPPushEnrollment/OTPViewModelTests.swift @@ -102,14 +102,9 @@ struct OTPViewModelTests { return data } - init() async throws { - Auth0UniversalComponentsSDKInitializer.reset() - } - @Test func testInit_initialState() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel(totpEnrollmentChallenge: totpEnrollmentChallenge, emailEnrollmentChallenge: nil, phoneEnrollmentChallenge: nil, type: .totp, delegate: nil) @@ -124,7 +119,6 @@ struct OTPViewModelTests { @Test func testConfirmEnrollment_TOTP_Success() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel(startPhoneEnrollmentUseCase: StartPhoneEnrollmentUseCase(session: makeMockSession()), @@ -162,7 +156,6 @@ struct OTPViewModelTests { @Test func testConfirmEnrollment_Email_Success() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel(startPhoneEnrollmentUseCase: StartPhoneEnrollmentUseCase(session: makeMockSession()), @@ -200,7 +193,6 @@ struct OTPViewModelTests { @Test func testConfirmEnrollment_Phone_Success() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel(confirmPhoneEnrollmentUseCase: ConfirmPhoneEnrollmentUseCase(session: makeMockSession()), @@ -235,7 +227,6 @@ struct OTPViewModelTests { @Test func testEmailOrSMS() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel(type: .sms, delegate: nil) @@ -246,7 +237,6 @@ struct OTPViewModelTests { @Test func navigationTitle() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel(type: .sms, delegate: nil) @@ -269,7 +259,6 @@ struct OTPViewModelTests { let mockTokenProvider = MockTokenProvider() let mockDelegate = MockRefreshDelegate() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel(totpEnrollmentChallenge: totpEnrollmentChallenge, emailEnrollmentChallenge: nil, phoneEnrollmentChallenge: nil, type: .totp, delegate: mockDelegate) @@ -283,7 +272,6 @@ struct OTPViewModelTests { let mockTokenProvider = MockTokenProvider() let mockDelegate = MockRefreshDelegate() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel( @@ -324,7 +312,6 @@ struct OTPViewModelTests { @Test func testConfirmEnrollment_TOTP_handlesAPIError() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel( @@ -371,7 +358,6 @@ struct OTPViewModelTests { @Test func testConfirmEnrollment_Email_handlesAPIError() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel( @@ -418,7 +404,6 @@ struct OTPViewModelTests { @Test func testConfirmEnrollment_Phone_handlesAPIError() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel( @@ -466,7 +451,6 @@ struct OTPViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel( @@ -492,7 +476,6 @@ struct OTPViewModelTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel( @@ -530,7 +513,6 @@ struct OTPViewModelTests { @Test func testOTPText_validation() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await OTPViewModel( diff --git a/Auth0UniversalComponentsTests/TOTPPushEnrollment/TOTPPushQRViewModelTests.swift b/Auth0UniversalComponentsTests/TOTPPushEnrollment/TOTPPushQRViewModelTests.swift index c69bec8f..4a41e6f9 100644 --- a/Auth0UniversalComponentsTests/TOTPPushEnrollment/TOTPPushQRViewModelTests.swift +++ b/Auth0UniversalComponentsTests/TOTPPushEnrollment/TOTPPushQRViewModelTests.swift @@ -71,7 +71,6 @@ struct TOTPPushQRCodeViewModelTests { @Test func testInit_initialState() async { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await TOTPPushQRCodeViewModel( startTOTPEnrollmentUseCase: StartTOTPEnrollmentUseCase(session: makeMockSession()), @@ -93,7 +92,6 @@ struct TOTPPushQRCodeViewModelTests { @Test func testFetchEnrollmentChallenge_TOTP_Success() async throws { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await TOTPPushQRCodeViewModel( @@ -122,7 +120,6 @@ struct TOTPPushQRCodeViewModelTests { @Test func testFetchEnrollmentChallenge_Push_Success() async throws { let mockTokenProvider = MockTokenProvider() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await TOTPPushQRCodeViewModel( @@ -151,7 +148,6 @@ struct TOTPPushQRCodeViewModelTests { @Test func testConfirmEnrollmentChallenge_TOTP_Success() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await TOTPPushQRCodeViewModel( @@ -196,7 +192,6 @@ struct TOTPPushQRCodeViewModelTests { @Test func testConfirmEnrollmentChallenge_Push_Success() async throws { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await TOTPPushQRCodeViewModel( @@ -238,7 +233,6 @@ struct TOTPPushQRCodeViewModelTests { func testNavigationTitle() async { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize(session: makeMockSession(), bundle: .main, domain: mockDomain, clientId: "", audience: "\(mockDomain)/me/", tokenProvider: mockTokenProvider) let viewModel = await TOTPPushQRCodeViewModel( startTOTPEnrollmentUseCase: StartTOTPEnrollmentUseCase(session: makeMockSession()), diff --git a/Auth0UniversalComponentsTests/Telemetry/MyAccountClientFactoryTests.swift b/Auth0UniversalComponentsTests/Telemetry/MyAccountClientFactoryTests.swift new file mode 100644 index 00000000..cd8a0714 --- /dev/null +++ b/Auth0UniversalComponentsTests/Telemetry/MyAccountClientFactoryTests.swift @@ -0,0 +1,233 @@ +import Testing +import Foundation +@testable import Auth0UniversalComponents +import Auth0 + +@Suite(.serialized) +struct MyAccountClientFactoryTests { + + private let mockDomain = "test-tenant.auth0.com" + private let mockToken = "mock_access_token" + + private func makeMockSession() -> URLSession { + let config = URLSessionConfiguration.ephemeral + config.protocolClasses = [MockURLProtocol.self] + return URLSession(configuration: config) + } + + @Test("Factory creates client with Auth0-Client header") + func testCreatesClientWithTelemetryHeader() async { + Auth0UniversalComponentsSDKInitializer.initialize( + session: makeMockSession(), + bundle: .main, + domain: mockDomain, + clientId: "test_client", + audience: "\(mockDomain)/me/", + tokenProvider: MockTokenProvider() + ) + + let session = makeMockSession() + var capturedRequest: URLRequest? + + MockURLProtocol.requestHandler = { request in + capturedRequest = request + let response = HTTPURLResponse( + url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: ["Location": "https://\(self.mockDomain)/me/v1/authentication-methods/totp%7Ctest"] + )! + let data = Data(""" + {"id":"totp|test","type":"totp","confirmed":true,"usage":["secondary"],"created_at":"2025-01-01T00:00:00.000Z"} + """.utf8) + return (response, data) + } + + let client = MyAccountClientFactory.create( + token: mockToken, + domain: mockDomain, + session: session + ) + + // Make a request to capture headers + _ = try? await client.authenticationMethods.enrollTOTP().start() + + #expect(capturedRequest != nil) + #expect(capturedRequest?.value(forHTTPHeaderField: "Auth0-Client") != nil) + } + + @Test("Factory overload without session works") + func testCreatesClientWithoutSession() { + Auth0UniversalComponentsSDKInitializer.initialize( + session: makeMockSession(), + bundle: .main, + domain: mockDomain, + clientId: "test_client", + audience: "\(mockDomain)/me/", + tokenProvider: MockTokenProvider() + ) + + // Just verify it doesn't crash + let client = MyAccountClientFactory.create( + token: mockToken, + domain: mockDomain + ) + + #expect(client != nil) + } + + @Test("Factory sends Auth0-Client header identifying UI Components SDK") + func testAuth0ClientHeaderContent() async { + Auth0UniversalComponentsSDKInitializer.initialize( + session: makeMockSession(), + bundle: .main, + domain: mockDomain, + clientId: "test_client", + audience: "\(mockDomain)/me/", + tokenProvider: MockTokenProvider() + ) + + let session = makeMockSession() + var capturedRequest: URLRequest? + + MockURLProtocol.requestHandler = { request in + capturedRequest = request + let response = HTTPURLResponse( + url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: ["Location": "https://\(self.mockDomain)/me/v1/authentication-methods/totp%7Ctest"] + )! + let data = Data(""" + {"id":"totp|test","type":"totp","confirmed":true,"usage":["secondary"],"created_at":"2025-01-01T00:00:00.000Z"} + """.utf8) + return (response, data) + } + + let client = MyAccountClientFactory.create( + token: mockToken, + domain: mockDomain, + session: session + ) + + _ = try? await client.authenticationMethods.enrollTOTP().start() + + guard let headerValue = capturedRequest?.value(forHTTPHeaderField: "Auth0-Client"), + let data = Data(base64URLEncoded: headerValue), + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let name = json["name"] as? String else { + Issue.record("Could not decode Auth0-Client header") + return + } + + #expect(name == Auth0UniversalComponents.libraryName) + } + + @Test("Factory includes SDK version in header") + func testAuth0ClientHeaderVersion() async { + Auth0UniversalComponentsSDKInitializer.initialize( + session: makeMockSession(), + bundle: .main, + domain: mockDomain, + clientId: "test_client", + audience: "\(mockDomain)/me/", + tokenProvider: MockTokenProvider() + ) + + let session = makeMockSession() + var capturedRequest: URLRequest? + + MockURLProtocol.requestHandler = { request in + capturedRequest = request + let response = HTTPURLResponse( + url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: ["Location": "https://\(self.mockDomain)/me/v1/authentication-methods/totp%7Ctest"] + )! + let data = Data(""" + {"id":"totp|test","type":"totp","confirmed":true,"usage":["secondary"],"created_at":"2025-01-01T00:00:00.000Z"} + """.utf8) + return (response, data) + } + + let client = MyAccountClientFactory.create( + token: mockToken, + domain: mockDomain, + session: session + ) + + _ = try? await client.authenticationMethods.enrollTOTP().start() + + guard let headerValue = capturedRequest?.value(forHTTPHeaderField: "Auth0-Client"), + let data = Data(base64URLEncoded: headerValue), + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let headerVersion = json["version"] as? String else { + Issue.record("Could not decode Auth0-Client header") + return + } + + #expect(headerVersion == Auth0UniversalComponents.version) + } + + @Test("Factory includes Auth0.swift core version in env") + func testAuth0ClientHeaderCoreVersion() async { + Auth0UniversalComponentsSDKInitializer.initialize( + session: makeMockSession(), + bundle: .main, + domain: mockDomain, + clientId: "test_client", + audience: "\(mockDomain)/me/", + tokenProvider: MockTokenProvider() + ) + + let session = makeMockSession() + var capturedRequest: URLRequest? + + MockURLProtocol.requestHandler = { request in + capturedRequest = request + let response = HTTPURLResponse( + url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: ["Location": "https://\(self.mockDomain)/me/v1/authentication-methods/totp%7Ctest"] + )! + let data = Data(""" + {"id":"totp|test","type":"totp","confirmed":true,"usage":["secondary"],"created_at":"2025-01-01T00:00:00.000Z"} + """.utf8) + return (response, data) + } + + let client = MyAccountClientFactory.create( + token: mockToken, + domain: mockDomain, + session: session + ) + + _ = try? await client.authenticationMethods.enrollTOTP().start() + + guard let headerValue = capturedRequest?.value(forHTTPHeaderField: "Auth0-Client"), + let data = Data(base64URLEncoded: headerValue), + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let env = json["env"] as? [String: String], + let coreVersion = env["core"] else { + Issue.record("Could not decode Auth0-Client header env") + return + } + + #expect(!coreVersion.isEmpty) + } +} + +private extension Data { + init?(base64URLEncoded string: String) { + var base64 = string + .replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + let remainder = base64.count % 4 + if remainder > 0 { + base64 += String(repeating: "=", count: 4 - remainder) + } + self.init(base64Encoded: base64) + } +} diff --git a/Auth0UniversalComponentsTests/Utilities/ErrorHandlerTests.swift b/Auth0UniversalComponentsTests/Utilities/ErrorHandlerTests.swift index fff078ba..d0bc9bad 100644 --- a/Auth0UniversalComponentsTests/Utilities/ErrorHandlerTests.swift +++ b/Auth0UniversalComponentsTests/Utilities/ErrorHandlerTests.swift @@ -90,7 +90,6 @@ struct ErrorHandlerTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -158,7 +157,6 @@ struct ErrorHandlerTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main, @@ -196,7 +194,6 @@ struct ErrorHandlerTests { let mockTokenProvider = MockTokenProvider() await NavigationStore.shared.reset() - Auth0UniversalComponentsSDKInitializer.reset() Auth0UniversalComponentsSDKInitializer.initialize( session: makeMockSession(), bundle: .main,