mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge pull request #3997 from vector-im/element_3995
Handle User-Interactive Authentication fallback
This commit is contained in:
commit
7970b3fd97
30 changed files with 1339 additions and 405 deletions
|
@ -9,6 +9,7 @@ Changes to be released in next version
|
|||
* Widgets: Support $matrix_room_id and $matrix_widget_id parameters (#3987).
|
||||
* matrix.to: Support room preview when the permalink has parameters (like "via=").
|
||||
* Avoid megolm share requests if the device is not verified (#3969)
|
||||
* Handle User-Interactive Authentication fallback (#3995).
|
||||
|
||||
🐛 Bugfix
|
||||
* Push: Fix PushKit crashes due to undecryptable call invites (#3986).
|
||||
|
|
|
@ -280,6 +280,13 @@
|
|||
B158253B2475350A00604D79 /* EventFormatter+DTCoreTextFix.m in Sources */ = {isa = PBXBuildFile; fileRef = B158253A2475350A00604D79 /* EventFormatter+DTCoreTextFix.m */; };
|
||||
B158E72125485B9E000A7F75 /* Base32Coder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B158E72025485B9E000A7F75 /* Base32Coder.swift */; };
|
||||
B158E72325485EAB000A7F75 /* JitsiJWTTokenBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B158E72225485EAB000A7F75 /* JitsiJWTTokenBuilder.swift */; };
|
||||
B1622D1825BF1A0000C73AE2 /* AuthenticationSessionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1622D1725BF1A0000C73AE2 /* AuthenticationSessionService.swift */; };
|
||||
B1622D1D25BF2B6800C73AE2 /* ReauthenticationCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1622D1A25BF2B6800C73AE2 /* ReauthenticationCoordinatorBridgePresenter.swift */; };
|
||||
B1622D1E25BF2B6800C73AE2 /* ReauthenticationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1622D1B25BF2B6800C73AE2 /* ReauthenticationCoordinator.swift */; };
|
||||
B1622D1F25BF2B6800C73AE2 /* ReauthenticationCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1622D1C25BF2B6800C73AE2 /* ReauthenticationCoordinatorType.swift */; };
|
||||
B1622D2125BF2B7B00C73AE2 /* ReauthenticationCoordinatorParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1622D2025BF2B7B00C73AE2 /* ReauthenticationCoordinatorParameters.swift */; };
|
||||
B1622D2325BF2E6500C73AE2 /* AuthenticationSessionParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1622D2225BF2E6500C73AE2 /* AuthenticationSessionParameters.swift */; };
|
||||
B1622D2525BF784300C73AE2 /* AuthenticationParametersBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1622D2425BF784300C73AE2 /* AuthenticationParametersBuilder.swift */; };
|
||||
B1664BC520F4E67600808783 /* FallbackViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1664BAD20F4E67500808783 /* FallbackViewController.xib */; };
|
||||
B1664BC620F4E67600808783 /* FallbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BAE20F4E67500808783 /* FallbackViewController.m */; };
|
||||
B1664BC720F4E67600808783 /* SharePresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1664BB220F4E67500808783 /* SharePresentingViewController.m */; };
|
||||
|
@ -318,6 +325,10 @@
|
|||
B169331520F3CAFC00746532 /* PublicRoomTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B169330F20F3CAFC00746532 /* PublicRoomTableViewCell.xib */; };
|
||||
B169331620F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = B169331220F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m */; };
|
||||
B169331720F3CBE000746532 /* RecentCellData.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932F920F3C51900746532 /* RecentCellData.m */; };
|
||||
B169746A25C21BDC00E6B3FB /* CrossSigningSetupCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B169746725C21BDC00E6B3FB /* CrossSigningSetupCoordinatorType.swift */; };
|
||||
B169746B25C21BDC00E6B3FB /* CrossSigningSetupCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B169746825C21BDC00E6B3FB /* CrossSigningSetupCoordinator.swift */; };
|
||||
B169746C25C21BDC00E6B3FB /* CrossSigningSetupCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B169746925C21BDC00E6B3FB /* CrossSigningSetupCoordinatorBridgePresenter.swift */; };
|
||||
B169746F25C21C1900E6B3FB /* CrossSigningSetupCoordinatorParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = B169746E25C21C1900E6B3FB /* CrossSigningSetupCoordinatorParameters.swift */; };
|
||||
B16C028D25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16C028C25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift */; };
|
||||
B16C029025ABC428008CA7B1 /* CustomRoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16C028F25ABC427008CA7B1 /* CustomRoundedButton.swift */; };
|
||||
B16C029225ABCD31008CA7B1 /* InviteFriendsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16C029125ABCD31008CA7B1 /* InviteFriendsPresenter.swift */; };
|
||||
|
@ -332,6 +343,8 @@
|
|||
B16DC23E258A8025004DAB1A /* ThemeIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC23D258A8025004DAB1A /* ThemeIdentifier.swift */; };
|
||||
B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */; };
|
||||
B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1798301211B13B3001FD722 /* OnBoardingManager.swift */; };
|
||||
B17DA77925C81A2B003FA4C1 /* AuthenticationSessionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17DA77825C81A2B003FA4C1 /* AuthenticationSessionServiceTests.swift */; };
|
||||
B17DA77B25C8B085003FA4C1 /* ReauthFallBackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17DA77A25C8B085003FA4C1 /* ReauthFallBackViewController.swift */; };
|
||||
B183226623F55D6B0035B2E8 /* CameraAccessManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183226523F55D6B0035B2E8 /* CameraAccessManager.swift */; };
|
||||
B183226823F561380035B2E8 /* CameraAccessAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183226723F561380035B2E8 /* CameraAccessAlertPresenter.swift */; };
|
||||
B183226C23F59F810035B2E8 /* CloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183226B23F59F810035B2E8 /* CloseButton.swift */; };
|
||||
|
@ -1348,6 +1361,13 @@
|
|||
B158253A2475350A00604D79 /* EventFormatter+DTCoreTextFix.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "EventFormatter+DTCoreTextFix.m"; sourceTree = "<group>"; };
|
||||
B158E72025485B9E000A7F75 /* Base32Coder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base32Coder.swift; sourceTree = "<group>"; };
|
||||
B158E72225485EAB000A7F75 /* JitsiJWTTokenBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiJWTTokenBuilder.swift; sourceTree = "<group>"; };
|
||||
B1622D1725BF1A0000C73AE2 /* AuthenticationSessionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationSessionService.swift; sourceTree = "<group>"; };
|
||||
B1622D1A25BF2B6800C73AE2 /* ReauthenticationCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReauthenticationCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
B1622D1B25BF2B6800C73AE2 /* ReauthenticationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReauthenticationCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1622D1C25BF2B6800C73AE2 /* ReauthenticationCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReauthenticationCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B1622D2025BF2B7B00C73AE2 /* ReauthenticationCoordinatorParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReauthenticationCoordinatorParameters.swift; sourceTree = "<group>"; };
|
||||
B1622D2225BF2E6500C73AE2 /* AuthenticationSessionParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationSessionParameters.swift; sourceTree = "<group>"; };
|
||||
B1622D2425BF784300C73AE2 /* AuthenticationParametersBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationParametersBuilder.swift; sourceTree = "<group>"; };
|
||||
B1664BAD20F4E67500808783 /* FallbackViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FallbackViewController.xib; sourceTree = "<group>"; };
|
||||
B1664BAE20F4E67500808783 /* FallbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FallbackViewController.m; sourceTree = "<group>"; };
|
||||
B1664BAF20F4E67500808783 /* FallbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FallbackViewController.h; sourceTree = "<group>"; };
|
||||
|
@ -1434,6 +1454,10 @@
|
|||
B169331020F3CAFC00746532 /* PublicRoomTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomTableViewCell.h; sourceTree = "<group>"; };
|
||||
B169331220F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PublicRoomsDirectoryDataSource.m; sourceTree = "<group>"; };
|
||||
B169331320F3CAFC00746532 /* PublicRoomsDirectoryDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomsDirectoryDataSource.h; sourceTree = "<group>"; };
|
||||
B169746725C21BDC00E6B3FB /* CrossSigningSetupCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossSigningSetupCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B169746825C21BDC00E6B3FB /* CrossSigningSetupCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossSigningSetupCoordinator.swift; sourceTree = "<group>"; };
|
||||
B169746925C21BDC00E6B3FB /* CrossSigningSetupCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossSigningSetupCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
B169746E25C21C1900E6B3FB /* CrossSigningSetupCoordinatorParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossSigningSetupCoordinatorParameters.swift; sourceTree = "<group>"; };
|
||||
B16C028C25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSchemeURLConstants.swift; sourceTree = "<group>"; };
|
||||
B16C028F25ABC427008CA7B1 /* CustomRoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRoundedButton.swift; sourceTree = "<group>"; };
|
||||
B16C029125ABCD31008CA7B1 /* InviteFriendsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteFriendsPresenter.swift; sourceTree = "<group>"; };
|
||||
|
@ -1448,6 +1472,8 @@
|
|||
B16DC23D258A8025004DAB1A /* ThemeIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeIdentifier.swift; sourceTree = "<group>"; };
|
||||
B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDPRConsentViewController.swift; sourceTree = "<group>"; };
|
||||
B1798301211B13B3001FD722 /* OnBoardingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingManager.swift; sourceTree = "<group>"; };
|
||||
B17DA77825C81A2B003FA4C1 /* AuthenticationSessionServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationSessionServiceTests.swift; sourceTree = "<group>"; };
|
||||
B17DA77A25C8B085003FA4C1 /* ReauthFallBackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReauthFallBackViewController.swift; sourceTree = "<group>"; };
|
||||
B183226523F55D6B0035B2E8 /* CameraAccessManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAccessManager.swift; sourceTree = "<group>"; };
|
||||
B183226723F561380035B2E8 /* CameraAccessAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAccessAlertPresenter.swift; sourceTree = "<group>"; };
|
||||
B183226B23F59F810035B2E8 /* CloseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseButton.swift; sourceTree = "<group>"; };
|
||||
|
@ -3026,6 +3052,18 @@
|
|||
path = ThreePidDetails;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1622D1925BF2B6800C73AE2 /* Reauthentication */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1622D1C25BF2B6800C73AE2 /* ReauthenticationCoordinatorType.swift */,
|
||||
B1622D1B25BF2B6800C73AE2 /* ReauthenticationCoordinator.swift */,
|
||||
B1622D2025BF2B7B00C73AE2 /* ReauthenticationCoordinatorParameters.swift */,
|
||||
B1622D1A25BF2B6800C73AE2 /* ReauthenticationCoordinatorBridgePresenter.swift */,
|
||||
B17DA77A25C8B085003FA4C1 /* ReauthFallBackViewController.swift */,
|
||||
);
|
||||
path = Reauthentication;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1664BAB20F4E67500808783 /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3270,6 +3308,17 @@
|
|||
path = DataSources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B169746D25C21BE100E6B3FB /* Setup */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B169746E25C21C1900E6B3FB /* CrossSigningSetupCoordinatorParameters.swift */,
|
||||
B169746825C21BDC00E6B3FB /* CrossSigningSetupCoordinator.swift */,
|
||||
B169746925C21BDC00E6B3FB /* CrossSigningSetupCoordinatorBridgePresenter.swift */,
|
||||
B169746725C21BDC00E6B3FB /* CrossSigningSetupCoordinatorType.swift */,
|
||||
);
|
||||
path = Setup;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B16DC2192587AED2004DAB1A /* SSO */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3517,6 +3566,7 @@
|
|||
B1560DA024B65A9500490F50 /* LaunchLoading */,
|
||||
B1B556CA20EE6C4C00210D55 /* TabBar */,
|
||||
B1B556F920EE6C4C00210D55 /* Authentication */,
|
||||
B1622D1925BF2B6800C73AE2 /* Reauthentication */,
|
||||
B17982FD2119FEA7001FD722 /* GDPR */,
|
||||
B1A6805224B7C60900E312CC /* MajorUpdate */,
|
||||
B1B5568420EE6C4C00210D55 /* Home */,
|
||||
|
@ -5012,6 +5062,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
EC3B066424AC6ADD000DF9BF /* CrossSigningService.swift */,
|
||||
B169746D25C21BE100E6B3FB /* Setup */,
|
||||
EC3B066524AC6ADD000DF9BF /* Banners */,
|
||||
);
|
||||
path = CrossSigning;
|
||||
|
@ -5222,6 +5273,9 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
EC711BB524A63C11008F830C /* AuthenticatedSessionViewControllerFactory.swift */,
|
||||
B1622D1725BF1A0000C73AE2 /* AuthenticationSessionService.swift */,
|
||||
B1622D2225BF2E6500C73AE2 /* AuthenticationSessionParameters.swift */,
|
||||
B1622D2425BF784300C73AE2 /* AuthenticationParametersBuilder.swift */,
|
||||
);
|
||||
path = AuthenticatedSession;
|
||||
sourceTree = "<group>";
|
||||
|
@ -5448,6 +5502,7 @@
|
|||
F083BB041E7005FD00A9B29C /* RiotTests.m */,
|
||||
B152C73022DF561E0041315A /* EmojiServiceTests.swift */,
|
||||
B1562CE42541D113002F8436 /* JitsiWellKnownTests.swift */,
|
||||
B17DA77825C81A2B003FA4C1 /* AuthenticationSessionServiceTests.swift */,
|
||||
F083BB071E70067700A9B29C /* Supporting Files */,
|
||||
);
|
||||
path = RiotTests;
|
||||
|
@ -6352,6 +6407,7 @@
|
|||
B1284E3E2535FEA6003529D7 /* SecureBackupSetupIntroViewModelType.swift in Sources */,
|
||||
B1FDF56021F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift in Sources */,
|
||||
B1B5573120EE6C4D00210D55 /* BugReportViewController.m in Sources */,
|
||||
B169746A25C21BDC00E6B3FB /* CrossSigningSetupCoordinatorType.swift in Sources */,
|
||||
ECFBD5CB250A7AAF00DD5F5A /* DirectoryRoomTableViewCellVM.swift in Sources */,
|
||||
324A2051225FC571004FE8B0 /* DeviceVerificationIncomingViewState.swift in Sources */,
|
||||
B1B4E9BF24D4703E004D5C33 /* BaseBubbleCell.swift in Sources */,
|
||||
|
@ -6458,6 +6514,7 @@
|
|||
B169330B20F3CA3A00746532 /* Contact.m in Sources */,
|
||||
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */,
|
||||
B113DD3D255E876A0022942A /* JitsiWellKnown.swift in Sources */,
|
||||
B1622D1E25BF2B6800C73AE2 /* ReauthenticationCoordinator.swift in Sources */,
|
||||
EC51E7AC2514D2E100AAE7DB /* RoomInfoListCoordinatorType.swift in Sources */,
|
||||
B1D4752A21EE52B10067973F /* KeyBackupSetupIntroViewController.swift in Sources */,
|
||||
B10A3E9424FE8254007C380F /* AppCoordinatorType.swift in Sources */,
|
||||
|
@ -6495,9 +6552,11 @@
|
|||
B1A6C111238BD236002882FD /* SlidingModalContainerView.swift in Sources */,
|
||||
B1DCC62D22E61EAF00625807 /* EmojiPickerViewCell.swift in Sources */,
|
||||
3232ABA8225730E100AD6A5C /* DeviceVerificationStartViewState.swift in Sources */,
|
||||
B1622D1F25BF2B6800C73AE2 /* ReauthenticationCoordinatorType.swift in Sources */,
|
||||
ECF57A5525091B4D004BBF9D /* RoomCreationParameters.swift in Sources */,
|
||||
B1B558D420EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B169331420F3CAFC00746532 /* PublicRoomTableViewCell.m in Sources */,
|
||||
B1622D1D25BF2B6800C73AE2 /* ReauthenticationCoordinatorBridgePresenter.swift in Sources */,
|
||||
32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */,
|
||||
3291DC8D23E0BFF10009732F /* SecurityViewController.m in Sources */,
|
||||
B18DEDD4243377C10075FEF7 /* KeyVerificationSelfVerifyWaitViewModelType.swift in Sources */,
|
||||
|
@ -6511,6 +6570,7 @@
|
|||
B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */,
|
||||
B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */,
|
||||
32F6B9692270623100BBA352 /* KeyVerificationDataLoadingCoordinator.swift in Sources */,
|
||||
B1622D1825BF1A0000C73AE2 /* AuthenticationSessionService.swift in Sources */,
|
||||
B125FE1D231D5DE400B72806 /* SettingsDiscoveryViewModel.swift in Sources */,
|
||||
32863A5A2384070300D07C4A /* RiotSharedSettings.swift in Sources */,
|
||||
B1B5594720EF7BD000210D55 /* RoomCollectionViewCell.m in Sources */,
|
||||
|
@ -6631,6 +6691,7 @@
|
|||
B1C45A8B232A8C2600165425 /* SettingsIdentityServerViewModel.swift in Sources */,
|
||||
EC711B8224A63B37008F830C /* SecretsSetupRecoveryPassphraseViewModelType.swift in Sources */,
|
||||
B12D7A0123E2462200FACEDC /* UserVerificationStartViewModel.swift in Sources */,
|
||||
B1622D2525BF784300C73AE2 /* AuthenticationParametersBuilder.swift in Sources */,
|
||||
B12C56EF2396CB5E00FAC6DE /* RoomMessageURLParser.swift in Sources */,
|
||||
EC711B9924A63B37008F830C /* SecretsRecoveryCoordinator.swift in Sources */,
|
||||
B1BEE73623DF44A60003A4CB /* UserVerificationSessionsStatusCoordinatorType.swift in Sources */,
|
||||
|
@ -6660,6 +6721,7 @@
|
|||
ECF57A5E2509265A004BBF9D /* InsettedTextField.swift in Sources */,
|
||||
B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */,
|
||||
32891D6B2264CBA300C82226 /* SimpleScreenTemplateViewController.swift in Sources */,
|
||||
B169746C25C21BDC00E6B3FB /* CrossSigningSetupCoordinatorBridgePresenter.swift in Sources */,
|
||||
EC711B7924A63B37008F830C /* SecretsSetupRecoveryKeyViewController.swift in Sources */,
|
||||
32FD755024D074C700BA7B37 /* CommonConfiguration.swift in Sources */,
|
||||
B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */,
|
||||
|
@ -6723,6 +6785,7 @@
|
|||
EC85D7162477DCD7002C44C9 /* KeyVerificationScanConfirmationViewAction.swift in Sources */,
|
||||
EC711BAF24A63B58008F830C /* SecureBackupSetupCoordinatorType.swift in Sources */,
|
||||
ECFBD5D4250A7AAF00DD5F5A /* ShowDirectoryCoordinatorType.swift in Sources */,
|
||||
B1622D2325BF2E6500C73AE2 /* AuthenticationSessionParameters.swift in Sources */,
|
||||
EC1CA89C24C9C9A200DE9EBF /* SetupBiometricsViewModel.swift in Sources */,
|
||||
B157FAA323264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewState.swift in Sources */,
|
||||
B1098BF921ECFE65000DDA48 /* KeyBackupSetupCoordinator.swift in Sources */,
|
||||
|
@ -6760,6 +6823,7 @@
|
|||
B14084CA23BF89310010F692 /* KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift in Sources */,
|
||||
B1DCC61D22E5E17100625807 /* EmojiPickerViewModelType.swift in Sources */,
|
||||
B1B5574120EE6C4D00210D55 /* RecentsViewController.m in Sources */,
|
||||
B1622D2125BF2B7B00C73AE2 /* ReauthenticationCoordinatorParameters.swift in Sources */,
|
||||
B1D250D82118AA0A000F4E93 /* RoomPredecessorBubbleCell.m in Sources */,
|
||||
B12676832523E4D100BE6B98 /* SecretsResetCoordinator.swift in Sources */,
|
||||
B1B5577120EE702800210D55 /* StickerPickerViewController.m in Sources */,
|
||||
|
@ -6831,6 +6895,7 @@
|
|||
B1B5596620EF9E9B00210D55 /* RoomTableViewCell.m in Sources */,
|
||||
ECFBD5EB250F97FC00DD5F5A /* RoomCreationWithPaginationCollapsedBubbleCell.m in Sources */,
|
||||
EC711B7D24A63B37008F830C /* SecretsSetupRecoveryPassphraseViewModel.swift in Sources */,
|
||||
B169746B25C21BDC00E6B3FB /* CrossSigningSetupCoordinator.swift in Sources */,
|
||||
EC711B8024A63B37008F830C /* SecretsSetupRecoveryPassphraseViewState.swift in Sources */,
|
||||
B1C45A89232A8C2600165425 /* SettingsIdentityServerViewController.swift in Sources */,
|
||||
EC85D7152477DCD7002C44C9 /* KeyVerificationScanConfirmationViewController.swift in Sources */,
|
||||
|
@ -6980,6 +7045,7 @@
|
|||
F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */,
|
||||
32DB557522FDADE50016329E /* ServiceTermsModalCoordinatorType.swift in Sources */,
|
||||
F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */,
|
||||
B17DA77B25C8B085003FA4C1 /* ReauthFallBackViewController.swift in Sources */,
|
||||
EC85D7272477DCF2002C44C9 /* KeyVerificationManuallyVerifyViewController.swift in Sources */,
|
||||
ECFBD5CF250A7AAF00DD5F5A /* DirectoryNetworkTableHeaderFooterView.swift in Sources */,
|
||||
B1D211E422C18E3800D939BD /* ReactionsMenuViewModelType.swift in Sources */,
|
||||
|
@ -7040,6 +7106,7 @@
|
|||
ECFBD5ED250F97FC00DD5F5A /* RoomCreationCollapsedBubbleCell.m in Sources */,
|
||||
B1DB4F06223015080065DBFA /* Character.swift in Sources */,
|
||||
EC1CA87724C82D0E00DE9EBF /* MemoryStore.swift in Sources */,
|
||||
B169746F25C21C1900E6B3FB /* CrossSigningSetupCoordinatorParameters.swift in Sources */,
|
||||
B1B9DEEE22EB34EF0065E677 /* ReactionHistoryViewAction.swift in Sources */,
|
||||
EC711BB624A63C11008F830C /* AuthenticatedSessionViewControllerFactory.swift in Sources */,
|
||||
B1C543A4239E98E400DCA1FA /* KeyVerificationCellInnerContentView.swift in Sources */,
|
||||
|
@ -7068,6 +7135,7 @@
|
|||
32FD754D24D06EBF00BA7B37 /* RiotSettings.swift in Sources */,
|
||||
B1562CE52541D113002F8436 /* JitsiWellKnownTests.swift in Sources */,
|
||||
32FD757124D2BEF700BA7B37 /* InfoPlist.swift in Sources */,
|
||||
B17DA77925C81A2B003FA4C1 /* AuthenticationSessionServiceTests.swift in Sources */,
|
||||
32FD756524D2AD5100BA7B37 /* BuildSettings.swift in Sources */,
|
||||
32FD755524D0754500BA7B37 /* AppConfiguration.swift in Sources */,
|
||||
32FD755124D074C700BA7B37 /* CommonConfiguration.swift in Sources */,
|
||||
|
|
|
@ -142,8 +142,8 @@
|
|||
// Social login
|
||||
|
||||
"social_login_list_title_continue" = "Continue with";
|
||||
"social_login_list_title_sign_in" = "Or login with";
|
||||
"social_login_list_title_sign_up" = "Or register with";
|
||||
"social_login_list_title_sign_in" = "Or";
|
||||
"social_login_list_title_sign_up" = "Or";
|
||||
|
||||
"social_login_button_title_continue" = "Continue with %@";
|
||||
"social_login_button_title_sign_in" = "Sign In with %@";
|
||||
|
|
|
@ -4302,11 +4302,11 @@ internal enum VectorL10n {
|
|||
internal static var socialLoginListTitleContinue: String {
|
||||
return VectorL10n.tr("Vector", "social_login_list_title_continue")
|
||||
}
|
||||
/// Or login with
|
||||
/// Or
|
||||
internal static var socialLoginListTitleSignIn: String {
|
||||
return VectorL10n.tr("Vector", "social_login_list_title_sign_in")
|
||||
}
|
||||
/// Or register with
|
||||
/// Or
|
||||
internal static var socialLoginListTitleSignUp: String {
|
||||
return VectorL10n.tr("Vector", "social_login_list_title_sign_up")
|
||||
}
|
||||
|
|
|
@ -15,167 +15,24 @@
|
|||
*/
|
||||
import Foundation
|
||||
|
||||
enum AuthenticatedSessionViewControllerFactoryError: Int, Error, CustomNSError {
|
||||
case flowNotSupported = 0
|
||||
|
||||
// MARK: - CustomNSError
|
||||
|
||||
static let errorDomain = "AuthenticatedSessionViewControllerFactoryErrorDomain"
|
||||
|
||||
var errorCode: Int { return self.rawValue }
|
||||
|
||||
var errorUserInfo: [String: Any] {
|
||||
let userInfo: [String: Any]
|
||||
|
||||
switch self {
|
||||
case .flowNotSupported:
|
||||
userInfo = [NSLocalizedDescriptionKey: VectorL10n.authenticatedSessionFlowNotSupported]
|
||||
}
|
||||
return userInfo
|
||||
}
|
||||
}
|
||||
|
||||
/// This class creates view controllers that can handle an authentication flow for given requests.
|
||||
@objcMembers
|
||||
final class AuthenticatedSessionViewControllerFactory: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
/// Create a view controller to handle an authentication flow for a given request.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - path: the request path.
|
||||
/// - httpMethod: the request http method.
|
||||
/// - title: the title to use in the view controller.
|
||||
/// - message: the information to display in the view controller.
|
||||
/// - onViewController: the block called when the view controller is ready. The caller must display it.
|
||||
/// - onAuthenticated: the block called when the user finished to enter their credentials.
|
||||
/// - onCancelled: the block called when the user cancelled the authentication.
|
||||
/// - onFailure: the blocked called on error.
|
||||
@discardableResult
|
||||
func viewController(forPath path: String,
|
||||
httpMethod: String,
|
||||
title: String?,
|
||||
message: String?,
|
||||
onViewController: @escaping (UIViewController) -> Void,
|
||||
onAuthenticated: @escaping ([String: Any]) -> Void,
|
||||
onCancelled: @escaping () -> Void,
|
||||
onFailure: @escaping (Error) -> Void) -> MXHTTPOperation {
|
||||
|
||||
// Get the authentication flow required for this API
|
||||
return session.matrixRestClient.authSessionForRequest(withMethod: httpMethod, path: path, parameters: [:], success: { [weak self] (authenticationSession) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let authenticationSession = authenticationSession, let flows = authenticationSession.flows else {
|
||||
onFailure(AuthenticatedSessionViewControllerFactoryError.flowNotSupported)
|
||||
return
|
||||
}
|
||||
|
||||
// Return the corresponding VC
|
||||
if self.hasPasswordFlow(inFlows: flows) {
|
||||
let authViewController = self.createPasswordViewController(title: title,
|
||||
message: message,
|
||||
authenticationSession: authenticationSession,
|
||||
onAuthenticated: onAuthenticated,
|
||||
onCancelled: onCancelled,
|
||||
onFailure: onFailure)
|
||||
onViewController(authViewController)
|
||||
} else {
|
||||
// Flow not supported yet
|
||||
onFailure(AuthenticatedSessionViewControllerFactoryError.flowNotSupported)
|
||||
}
|
||||
|
||||
}, failure: { (error) in
|
||||
guard let error = error else {
|
||||
return
|
||||
}
|
||||
|
||||
onFailure(error)
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if we support the authentication flow for a given request.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - path: the request path.
|
||||
/// - httpMethod: the request http method.
|
||||
/// - onCancelled: the block called when the user cancelled the authentication.
|
||||
/// - onFailure: the blocked called on error.
|
||||
func hasSupport(forPath path: String,
|
||||
httpMethod: String,
|
||||
success: @escaping (Bool) -> Void,
|
||||
failure: @escaping (Error) -> Void) -> MXHTTPOperation {
|
||||
|
||||
// Get the authentication flow required for this API
|
||||
return session.matrixRestClient.authSessionForRequest(withMethod: httpMethod, path: path, parameters: [:], success: { [weak self] (authenticationSession) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let authenticationSession = authenticationSession, let flows = authenticationSession.flows else {
|
||||
success(false)
|
||||
return
|
||||
}
|
||||
|
||||
// Return the corresponding VC
|
||||
if self.hasPasswordFlow(inFlows: flows) {
|
||||
success(true)
|
||||
} else {
|
||||
// Flow not supported yet
|
||||
success(false)
|
||||
}
|
||||
|
||||
}, failure: { (error) in
|
||||
guard let error = error else {
|
||||
return
|
||||
}
|
||||
|
||||
failure(error)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
// MARK: - Password flow
|
||||
|
||||
private func hasPasswordFlow(inFlows flows: [MXLoginFlow]) -> Bool {
|
||||
for flow in flows {
|
||||
if flow.type == kMXLoginFlowTypePassword || flow.stages.contains(kMXLoginFlowTypePassword) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private func createPasswordViewController(
|
||||
/// Create a view controller to handle a password authentication.
|
||||
/// - Parameters:
|
||||
/// - title: the title to use in the view controller.
|
||||
/// - message: the information to display in the view controller.
|
||||
/// - onPasswordEntered: the closure called when the enter the password.
|
||||
/// - onCancelled: the closure called when the user cancelled the authentication.
|
||||
/// - Returns: the password authentication view controller
|
||||
func createPasswordViewController(
|
||||
title: String?,
|
||||
message: String?,
|
||||
authenticationSession: MXAuthenticationSession,
|
||||
onAuthenticated: @escaping ([String: Any]) -> Void,
|
||||
onCancelled: @escaping () -> Void,
|
||||
onFailure: @escaping (Error) -> Void) -> UIViewController {
|
||||
onPasswordEntered: @escaping (String) -> Void,
|
||||
onCancelled: @escaping () -> Void) -> UIViewController {
|
||||
|
||||
// Use a simple UIAlertController as before
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
@ -196,29 +53,9 @@ final class AuthenticatedSessionViewControllerFactory: NSObject {
|
|||
// Should not happen
|
||||
return
|
||||
}
|
||||
|
||||
guard let authParams = self.createAuthParams(password: password, authenticationSession: authenticationSession) else {
|
||||
onFailure(AuthenticatedSessionViewControllerFactoryError.flowNotSupported)
|
||||
return
|
||||
}
|
||||
|
||||
onAuthenticated(authParams)
|
||||
onPasswordEntered(password)
|
||||
}))
|
||||
|
||||
return alertController
|
||||
}
|
||||
|
||||
private func createAuthParams(password: String,
|
||||
authenticationSession: MXAuthenticationSession) -> [String: Any]? {
|
||||
guard let userId = self.session.myUserId, let session = authenticationSession.session else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return [
|
||||
"type": kMXLoginFlowTypePassword,
|
||||
"session": session,
|
||||
"user": userId,
|
||||
"password": password
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Copyright 2020 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Build authentication parameters depending on login type
|
||||
final class AuthenticationParametersBuilder {
|
||||
|
||||
func buildPasswordParameters(sessionId: String,
|
||||
userId: String,
|
||||
password: String) -> [String: Any]? {
|
||||
return [
|
||||
"type": MXLoginFlowType.password.identifier,
|
||||
"session": sessionId,
|
||||
"user": userId,
|
||||
"password": password
|
||||
]
|
||||
}
|
||||
|
||||
func buildTokenParameters(with loginToken: String) -> [String: Any] {
|
||||
return [
|
||||
"type": MXLoginFlowType.token.identifier,
|
||||
"token": loginToken
|
||||
]
|
||||
}
|
||||
|
||||
func buildOAuthParameters(with sessionId: String) -> [String: Any] {
|
||||
return [
|
||||
"session": sessionId
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Copyright 2020 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// AuthenticationSessionParameters represents authenticated API endpoint parameters.
|
||||
@objcMembers
|
||||
class AuthenticationSessionParameters: NSObject {
|
||||
|
||||
let path: String
|
||||
let httpMethod: String
|
||||
|
||||
init(path: String, httpMethod: String) {
|
||||
self.path = path
|
||||
self.httpMethod = httpMethod
|
||||
super.init()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
//
|
||||
// Copyright 2020 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// AuthenticationSessionService errors
|
||||
enum AuthenticationSessionServiceError: Int, Error {
|
||||
case flowNotSupported = 0
|
||||
}
|
||||
|
||||
extension AuthenticationSessionServiceError: CustomNSError {
|
||||
static let errorDomain = "AuthenticationSessionServiceErrorDomain"
|
||||
|
||||
var errorCode: Int {
|
||||
return self.rawValue
|
||||
}
|
||||
|
||||
var errorUserInfo: [String: Any] {
|
||||
let userInfo: [String: Any]
|
||||
|
||||
switch self {
|
||||
case .flowNotSupported:
|
||||
userInfo = [NSLocalizedDescriptionKey: VectorL10n.authenticatedSessionFlowNotSupported]
|
||||
}
|
||||
return userInfo
|
||||
}
|
||||
}
|
||||
|
||||
/// AuthenticationSessionResult indicates the success state after checking if an API endpoint needs authentication
|
||||
enum AuthenticationSessionResult {
|
||||
case authenticationNotNeeded
|
||||
case authenticationNeeded(_ authenticationSession: MXAuthenticationSession)
|
||||
}
|
||||
|
||||
/// AuthenticationSessionService enables to check if an API endpoint needs authentication.
|
||||
@objcMembers
|
||||
final class AuthenticationSessionService: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Check if API endpoint requires authentication and get authentication session if needed
|
||||
/// - Parameters:
|
||||
/// - authenticationSessionParameters: API endpoint parameters
|
||||
/// - completion: The closure executed the operation completes. AuthenticationSessionResult indicates
|
||||
/// - Returns: A `MXHTTPOperation` instance.
|
||||
@discardableResult
|
||||
func authenticationSession(for authenticationSessionParameters: AuthenticationSessionParameters, completion: @escaping (Result<AuthenticationSessionResult, Error>) -> Void) -> MXHTTPOperation {
|
||||
|
||||
return self.authenticationSession(for: authenticationSessionParameters) { (authenticationSession) in
|
||||
|
||||
let authenticationResult: AuthenticationSessionResult
|
||||
|
||||
if let authenticationSession = authenticationSession {
|
||||
authenticationResult = .authenticationNeeded(authenticationSession)
|
||||
} else {
|
||||
authenticationResult = .authenticationNotNeeded
|
||||
}
|
||||
|
||||
completion(.success(authenticationResult))
|
||||
} failure: { (error) in
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if API endpoint requires authentication.
|
||||
/// - Parameters:
|
||||
/// - authenticationSessionParameters: API endpoint parameters
|
||||
/// - completion: The closure executed the operation completes.
|
||||
/// - Returns: A `MXHTTPOperation` instance.
|
||||
@discardableResult
|
||||
func canAuthenticate(for authenticationSessionParameters: AuthenticationSessionParameters, completion: @escaping (Result<Bool, Error>) -> Void) -> MXHTTPOperation {
|
||||
|
||||
return self.authenticationSession(for: authenticationSessionParameters) { (result) in
|
||||
switch result {
|
||||
case .success(let authenticationSessionResult):
|
||||
let canAuthenticate: Bool
|
||||
|
||||
switch authenticationSessionResult {
|
||||
case .authenticationNotNeeded:
|
||||
canAuthenticate = true
|
||||
case .authenticationNeeded(let authenticationSession):
|
||||
canAuthenticate = self.canAuthenticate(with: authenticationSession)
|
||||
}
|
||||
|
||||
completion(.success(canAuthenticate))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if API endpoint requires authentication.
|
||||
/// This method is compatible with Objective-C
|
||||
/// - Parameters:
|
||||
/// - authenticationSessionParameters: API endpoint parameters
|
||||
/// - success: The closure executed the operation succeed. Get an MXAuthenticationSession if authentication is required or nil if there is no authentication needed.
|
||||
/// - failure: The closure executed the operation fails.
|
||||
/// - Returns: A `MXHTTPOperation` instance.
|
||||
@discardableResult
|
||||
func authenticationSession(for authenticationSessionParameters: AuthenticationSessionParameters,
|
||||
success: @escaping (MXAuthenticationSession?) -> Void,
|
||||
failure: @escaping (Error) -> Void) -> MXHTTPOperation {
|
||||
// Get the authentication flow required for this API
|
||||
return self.session.matrixRestClient.authSessionForRequest(withMethod: authenticationSessionParameters.httpMethod, path: authenticationSessionParameters.path, parameters: [:], success: { [weak self] (authenticationSession) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let authenticationSession = authenticationSession {
|
||||
|
||||
if let flows = authenticationSession.flows {
|
||||
// Check if a supported flow exists
|
||||
if self.isThereAKnownFlow(inFlows: flows) {
|
||||
success(authenticationSession)
|
||||
} else if self.firstUncompletedStageAuthenticationFallbackURL(for: authenticationSession) != nil {
|
||||
success(authenticationSession)
|
||||
} else {
|
||||
// Flow not supported
|
||||
failure(AuthenticationSessionServiceError.flowNotSupported)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No need to authenticate
|
||||
success(nil)
|
||||
}
|
||||
}, failure: { (error) in
|
||||
guard let error = error else {
|
||||
return
|
||||
}
|
||||
failure(error)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the authentication fallback URL for the first uncompleted stage found.
|
||||
/// - Parameter authenticationSession: An authentication session for a given request.
|
||||
/// - Returns: The fallback URL for the first uncompleted stage found.
|
||||
func firstUncompletedStageAuthenticationFallbackURL(for authenticationSession: MXAuthenticationSession) -> URL? {
|
||||
guard let sessiondId = authenticationSession.session, let firstUncompletedStageIdentifier = self.firstUncompletedFlowIdentifier(in: authenticationSession) else {
|
||||
return nil
|
||||
}
|
||||
return self.authenticationFallbackURL(for: firstUncompletedStageIdentifier, sessionId: sessiondId)
|
||||
}
|
||||
|
||||
/// Build UIA fallback authentication URL for a given stage (https://matrix.org/docs/spec/client_server/latest#fallback)
|
||||
/// - Parameters:
|
||||
/// - flowIdentifier: The login type to authenticate with.
|
||||
/// - sessionId: The the ID of the session given by the homeserver.
|
||||
/// - Returns: a `MXHTTPOperation` instance.
|
||||
func authenticationFallbackURL(for flowIdentifier: String, sessionId: String) -> URL? {
|
||||
guard let homeserverStringURL = self.session.matrixRestClient.credentials.homeServer, let homeserverURL = URL(string: homeserverStringURL) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let fallbackPath = "\(kMXAPIPrefixPathR0)/auth/\(flowIdentifier)/fallback/web?session=\(sessionId)"
|
||||
|
||||
return URL(string: fallbackPath, relativeTo: homeserverURL)
|
||||
}
|
||||
|
||||
/// Find the first uncompleted login flow stage in a MXauthenticationSession.
|
||||
/// - Parameter authenticationSession: An authentication session for a given request.
|
||||
/// - Returns: Uncompleted login flow stage identifier.
|
||||
func firstUncompletedFlowIdentifier(in authenticationSession: MXAuthenticationSession) -> String? {
|
||||
|
||||
let completedStages = authenticationSession.completed ?? []
|
||||
|
||||
guard let flows = authenticationSession.flows else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove nil values
|
||||
let allNonNullStages = flows.compactMap { $0.stages }
|
||||
|
||||
// Make a flat array of all stages
|
||||
let allStages: [String] = allNonNullStages.flatMap { $0 }
|
||||
|
||||
// Keep stages order
|
||||
let uncompletedStages = NSMutableOrderedSet(array: allStages)
|
||||
|
||||
// Keep uncompleted stages
|
||||
let completedStagesSet = NSOrderedSet(array: completedStages)
|
||||
uncompletedStages.minus(completedStagesSet)
|
||||
|
||||
let firstUncompletedFlowIdentifier = uncompletedStages.firstObject as? String
|
||||
return firstUncompletedFlowIdentifier
|
||||
}
|
||||
|
||||
/// Check if an array of login flows contains "m.login.password" flow.
|
||||
func hasPasswordFlow(inFlows flows: [MXLoginFlow]) -> Bool {
|
||||
for flow in flows {
|
||||
if flow.type == MXLoginFlowType.password.identifier || flow.stages.contains(MXLoginFlowType.password.identifier) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func isThereAKnownFlow(inFlows flows: [MXLoginFlow]) -> Bool {
|
||||
return self.hasPasswordFlow(inFlows: flows)
|
||||
}
|
||||
|
||||
private func canAuthenticate(with authenticationSession: MXAuthenticationSession) -> Bool {
|
||||
|
||||
if self.isThereAKnownFlow(inFlows: authenticationSession.flows) {
|
||||
return true
|
||||
}
|
||||
|
||||
if self.firstUncompletedStageAuthenticationFallbackURL(for: authenticationSession) != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -68,11 +68,17 @@ final class SSOAuthenticationPresenter: NSObject {
|
|||
|
||||
self.presentingViewController = presentingViewController
|
||||
|
||||
// SFAuthenticationSession and ASWebAuthenticationSession doesn't work with guided access (rdar://48376122)
|
||||
if UIAccessibility.isGuidedAccessEnabled {
|
||||
self.presentSafariViewController(with: authenticationURL, animated: animated)
|
||||
// NOTE: By using SFAuthenticationSession the consent alert show product name instead of display name. Fallback to SFSafariViewController instead in order to not disturb users with "Riot" wording at the moment.
|
||||
// (https://stackoverflow.com/questions/49860338/why-does-sfauthenticationsession-consent-alert-show-xcode-project-name-instead-o)
|
||||
if #available(iOS 13.0, *) {
|
||||
// SFAuthenticationSession and ASWebAuthenticationSession doesn't work with guided access (rdar://48376122)
|
||||
if UIAccessibility.isGuidedAccessEnabled {
|
||||
self.presentSafariViewController(with: authenticationURL, animated: animated)
|
||||
} else {
|
||||
self.startAuthenticationSession(with: authenticationURL)
|
||||
}
|
||||
} else {
|
||||
self.startAuthenticationSession(with: authenticationURL)
|
||||
self.presentSafariViewController(with: authenticationURL, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,12 @@ import Foundation
|
|||
@objcMembers
|
||||
final class CrossSigningService: NSObject {
|
||||
|
||||
private var authenticatedSessionFactory: AuthenticatedSessionViewControllerFactory?
|
||||
// MARK - Properties
|
||||
|
||||
private var supportSetupKeyVerificationByUser: [String: Bool] = [:] // Cached server response
|
||||
private var authenticationSessionService: AuthenticationSessionService?
|
||||
|
||||
// MARK - Public
|
||||
|
||||
@discardableResult
|
||||
func canSetupCrossSigning(for session: MXSession, success: @escaping ((Bool) -> Void), failure: @escaping ((Error) -> Void)) -> MXHTTPOperation? {
|
||||
|
@ -39,25 +43,24 @@ final class CrossSigningService: NSObject {
|
|||
return nil
|
||||
}
|
||||
|
||||
let authenticatedSessionFactory = AuthenticatedSessionViewControllerFactory(session: session)
|
||||
let authenticationSessionService = AuthenticationSessionService(session: session)
|
||||
|
||||
self.authenticatedSessionFactory = authenticatedSessionFactory
|
||||
self.authenticationSessionService = authenticationSessionService
|
||||
|
||||
let authenticationSessionParameters = self.setupCrossSigningAuthenticationSessionParameters()
|
||||
|
||||
let path = "\(kMXAPIPrefixPathUnstable)/keys/device_signing/upload"
|
||||
|
||||
return authenticatedSessionFactory.hasSupport(forPath: path, httpMethod: "POST", success: { [weak self] succeeded in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.authenticatedSessionFactory = nil
|
||||
self.supportSetupKeyVerificationByUser[userId] = succeeded
|
||||
success(succeeded)
|
||||
}, failure: { [weak self] error in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.authenticatedSessionFactory = nil
|
||||
return authenticationSessionService.canAuthenticate(for: authenticationSessionParameters) { (result) in
|
||||
switch result {
|
||||
case .success(let succeeded):
|
||||
success(succeeded)
|
||||
case .failure(let error):
|
||||
failure(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setupCrossSigningAuthenticationSessionParameters() -> AuthenticationSessionParameters {
|
||||
let path = "\(kMXAPIPrefixPathUnstable)/keys/device_signing/upload"
|
||||
return AuthenticationSessionParameters(path: path, httpMethod: "POST")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh CrossSigning CrossSigningSetup
|
||||
/*
|
||||
Copyright 2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
final class CrossSigningSetupCoordinator: CrossSigningSetupCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: CrossSigningSetupCoordinatorParameters
|
||||
private let crossSigningService: CrossSigningService
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: CrossSigningSetupCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: CrossSigningSetupCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
self.crossSigningService = CrossSigningService()
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
self.showReauthentication()
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.parameters.presenter.toPresentable()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func showReauthentication() {
|
||||
|
||||
let authenticationSessionParameters = self.crossSigningService.setupCrossSigningAuthenticationSessionParameters()
|
||||
|
||||
let reauthenticationParameters = ReauthenticationCoordinatorParameters(session: parameters.session,
|
||||
presenter: parameters.presenter,
|
||||
title: parameters.title,
|
||||
message: parameters.message,
|
||||
authenticationSessionParameters: authenticationSessionParameters)
|
||||
|
||||
let coordinator = ReauthenticationCoordinator(parameters: reauthenticationParameters)
|
||||
coordinator.delegate = self
|
||||
self.add(childCoordinator: coordinator)
|
||||
|
||||
coordinator.start()
|
||||
}
|
||||
|
||||
private func setupCrossSigning(with authenticationParameters: [String: Any]) {
|
||||
guard let crossSigning = self.parameters.session.crypto.crossSigning else {
|
||||
return
|
||||
}
|
||||
|
||||
crossSigning.setup(withAuthParams: authenticationParameters) { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.crossSigningSetupCoordinatorDidComplete(self)
|
||||
} failure: { [weak self] error in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.crossSigningSetupCoordinator(self, didFailWithError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReauthenticationCoordinatorDelegate
|
||||
extension CrossSigningSetupCoordinator: ReauthenticationCoordinatorDelegate {
|
||||
|
||||
func reauthenticationCoordinatorDidComplete(_ coordinator: ReauthenticationCoordinatorType, withAuthenticationParameters authenticationParameters: [String: Any]?) {
|
||||
self.setupCrossSigning(with: authenticationParameters ?? [:])
|
||||
}
|
||||
|
||||
func reauthenticationCoordinatorDidCancel(_ coordinator: ReauthenticationCoordinatorType) {
|
||||
self.delegate?.crossSigningSetupCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
func reauthenticationCoordinator(_ coordinator: ReauthenticationCoordinatorType, didFailWithError error: Error) {
|
||||
self.delegate?.crossSigningSetupCoordinator(self, didFailWithError: error)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh CrossSigning CrossSigningSetup
|
||||
/*
|
||||
Copyright 2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// CrossSigningSetupCoordinatorBridgePresenter enables to start CrossSigningSetupCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers).
|
||||
/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
|
||||
@objcMembers
|
||||
final class CrossSigningSetupCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private var coordinator: CrossSigningSetupCoordinator?
|
||||
|
||||
private var didComplete: (() -> Void)?
|
||||
private var didCancel: (() -> Void)?
|
||||
private var didFail: ((Error) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(with title: String,
|
||||
message: String,
|
||||
from viewController: UIViewController,
|
||||
animated: Bool,
|
||||
success: @escaping () -> Void,
|
||||
cancel: @escaping () -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
|
||||
self.didComplete = success
|
||||
self.didCancel = cancel
|
||||
self.didFail = failure
|
||||
|
||||
let parameters = CrossSigningSetupCoordinatorParameters(session: self.session, presenter: viewController, title: title, message: message)
|
||||
|
||||
let crossSigningSetupCoordinator = CrossSigningSetupCoordinator(parameters: parameters)
|
||||
crossSigningSetupCoordinator.delegate = self
|
||||
crossSigningSetupCoordinator.start()
|
||||
|
||||
self.coordinator = crossSigningSetupCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
|
||||
self.resetCompletions()
|
||||
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func resetCompletions() {
|
||||
self.didComplete = nil
|
||||
self.didCancel = nil
|
||||
self.didFail = nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CrossSigningSetupCoordinatorDelegate
|
||||
extension CrossSigningSetupCoordinatorBridgePresenter: CrossSigningSetupCoordinatorDelegate {
|
||||
func crossSigningSetupCoordinatorDidComplete(_ coordinator: CrossSigningSetupCoordinatorType) {
|
||||
self.didComplete?()
|
||||
}
|
||||
|
||||
func crossSigningSetupCoordinatorDidCancel(_ coordinator: CrossSigningSetupCoordinatorType) {
|
||||
self.didCancel?()
|
||||
}
|
||||
|
||||
func crossSigningSetupCoordinator(_ coordinator: CrossSigningSetupCoordinatorType, didFailWithError error: Error) {
|
||||
self.didFail?(error)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Copyright 2020 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// CrossSigningSetupCoordinator input parameters
|
||||
@objcMembers
|
||||
class CrossSigningSetupCoordinatorParameters: NSObject {
|
||||
|
||||
/// The Matrix session
|
||||
let session: MXSession
|
||||
|
||||
/// The presenter used to show authentication screen(s)
|
||||
let presenter: Presentable
|
||||
|
||||
/// The title to use in the authentication screen if present.
|
||||
let title: String?
|
||||
|
||||
/// The message to use in the authentication screen if present.
|
||||
let message: String?
|
||||
|
||||
init(session: MXSession,
|
||||
presenter: Presentable,
|
||||
title: String?,
|
||||
message: String?) {
|
||||
self.session = session
|
||||
self.presenter = presenter
|
||||
self.title = title
|
||||
self.message = message
|
||||
|
||||
super.init()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh CrossSigning CrossSigningSetup
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol CrossSigningSetupCoordinatorDelegate: class {
|
||||
func crossSigningSetupCoordinatorDidComplete(_ coordinator: CrossSigningSetupCoordinatorType)
|
||||
func crossSigningSetupCoordinatorDidCancel(_ coordinator: CrossSigningSetupCoordinatorType)
|
||||
func crossSigningSetupCoordinator(_ coordinator: CrossSigningSetupCoordinatorType, didFailWithError error: Error)
|
||||
}
|
||||
|
||||
/// `CrossSigningSetupCoordinatorType` is a protocol describing a Coordinator that handles cross signing setup navigation flow.
|
||||
protocol CrossSigningSetupCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: CrossSigningSetupCoordinatorDelegate? { get }
|
||||
}
|
|
@ -44,7 +44,7 @@
|
|||
@property (nonatomic, strong) SecureBackupBannerCell *secureBackupBannerPrototypeCell;
|
||||
|
||||
@property (nonatomic, strong) CrossSigningSetupBannerCell *keyVerificationSetupBannerPrototypeCell;
|
||||
@property (nonatomic, strong) AuthenticatedSessionViewControllerFactory *authenticatedSessionViewControllerFactory;
|
||||
@property (nonatomic, strong) CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -736,68 +736,47 @@
|
|||
message:(NSString*)message
|
||||
success:(void (^)(void))success
|
||||
failure:(void (^)(NSError *error))failure
|
||||
|
||||
{
|
||||
__block UIViewController *viewController;
|
||||
[self startActivityIndicator];
|
||||
self.view.userInteractionEnabled = NO;
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
void (^animationCompletion)(void) = ^void () {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self stopActivityIndicator];
|
||||
self.view.userInteractionEnabled = YES;
|
||||
[self.crossSigningSetupCoordinatorBridgePresenter dismissWithAnimated:YES completion:^{}];
|
||||
self.crossSigningSetupCoordinatorBridgePresenter = nil;
|
||||
};
|
||||
|
||||
// Get credentials to set up cross-signing
|
||||
NSString *path = [NSString stringWithFormat:@"%@/keys/device_signing/upload", kMXAPIPrefixPathUnstable];
|
||||
self.authenticatedSessionViewControllerFactory = [[AuthenticatedSessionViewControllerFactory alloc] initWithSession:self.mainSession];
|
||||
[self.authenticatedSessionViewControllerFactory viewControllerForPath:path
|
||||
httpMethod:@"POST"
|
||||
title:title
|
||||
message:message
|
||||
onViewController:^(UIViewController * _Nonnull theViewController)
|
||||
{
|
||||
viewController = theViewController;
|
||||
[self presentViewController:viewController animated:YES completion:nil];
|
||||
|
||||
} onAuthenticated:^(NSDictionary * _Nonnull authParams) {
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
|
||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
||||
if (crossSigning)
|
||||
{
|
||||
[crossSigning setupWithAuthParams:authParams success:^{
|
||||
animationCompletion();
|
||||
|
||||
// TODO: Remove this line and refresh key verification setup banner by listening to a local notification cross-signing state change (Add this behavior into the SDK).
|
||||
[self->recentsDataSource setDelegate:self andRecentsDataSourceMode:RecentsDataSourceModeHome];
|
||||
|
||||
[self refreshRecentsTable];
|
||||
success();
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
animationCompletion();
|
||||
[self refreshRecentsTable];
|
||||
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
failure(error);
|
||||
}];
|
||||
}
|
||||
|
||||
} onCancelled:^{
|
||||
animationCompletion();
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
failure(nil);
|
||||
} onFailure:^(NSError * _Nonnull error) {
|
||||
|
||||
animationCompletion();
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
failure(error);
|
||||
}];
|
||||
CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter = [[CrossSigningSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
|
||||
|
||||
[crossSigningSetupCoordinatorBridgePresenter presentWith:title
|
||||
message:message
|
||||
from:self
|
||||
animated:YES
|
||||
success:^{
|
||||
animationCompletion();
|
||||
|
||||
// TODO: Remove this line and refresh key verification setup banner by listening to a local notification cross-signing state change (Add this behavior into the SDK).
|
||||
[self->recentsDataSource setDelegate:self andRecentsDataSourceMode:RecentsDataSourceModeHome];
|
||||
[self refreshRecentsTable];
|
||||
|
||||
success();
|
||||
} cancel:^{
|
||||
animationCompletion();
|
||||
failure(nil);
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
animationCompletion();
|
||||
[self refreshRecentsTable];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
failure(error);
|
||||
}];
|
||||
|
||||
self.crossSigningSetupCoordinatorBridgePresenter = crossSigningSetupCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
#pragma mark - Empty view management
|
||||
|
|
|
@ -19,8 +19,7 @@ import Foundation
|
|||
final class KeyVerificationService {
|
||||
|
||||
private let cameraAccessManager: CameraAccessManager
|
||||
|
||||
private var authenticatedSessionFactory: AuthenticatedSessionViewControllerFactory?
|
||||
|
||||
private var supportSetupKeyVerificationByUser: [String: Bool] = [:] // Cached server response
|
||||
|
||||
init() {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/// View controller used for User-Interactive Authentication fallback (https://matrix.org/docs/spec/client_server/latest#fallback)
|
||||
final class ReauthFallBackViewController: AuthFallBackViewController, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var didValidate: (() -> Void)?
|
||||
var didCancel: (() -> Void)?
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme = ThemeService.shared().theme
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
self.setupNavigationBar()
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
private func setupNavigationBar() {
|
||||
let doneBarButtonItem = MXKBarButtonItem(title: VectorL10n.close, style: .plain) { [weak self] in
|
||||
self?.didValidate?()
|
||||
}
|
||||
self.navigationItem.leftBarButtonItem = doneBarButtonItem
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||
extension ReauthFallBackViewController: UIAdaptivePresentationControllerDelegate {
|
||||
|
||||
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
|
||||
self.didCancel?()
|
||||
}
|
||||
}
|
157
Riot/Modules/Reauthentication/ReauthenticationCoordinator.swift
Normal file
157
Riot/Modules/Reauthentication/ReauthenticationCoordinator.swift
Normal file
|
@ -0,0 +1,157 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Reauthentication Reauthentication
|
||||
/*
|
||||
Copyright 2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
enum ReauthenticationCoordinatorError: Error {
|
||||
case failToBuildPasswordParameters
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
final class ReauthenticationCoordinator: ReauthenticationCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: ReauthenticationCoordinatorParameters
|
||||
private let authenticationSessionService: AuthenticationSessionService
|
||||
private let authenticationParametersBuilder: AuthenticationParametersBuilder
|
||||
private let authenticatedSessionViewControllerFactory: AuthenticatedSessionViewControllerFactory
|
||||
|
||||
private var ssoAuthenticationPresenter: SSOAuthenticationPresenter?
|
||||
|
||||
private var authenticationSession: SSOAuthentificationSessionProtocol?
|
||||
|
||||
private var presentingViewController: UIViewController {
|
||||
return self.parameters.presenter.toPresentable()
|
||||
}
|
||||
|
||||
private weak var passwordViewController: UIViewController?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: ReauthenticationCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: ReauthenticationCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
self.authenticationSessionService = AuthenticationSessionService(session: parameters.session)
|
||||
self.authenticationParametersBuilder = AuthenticationParametersBuilder()
|
||||
self.authenticatedSessionViewControllerFactory = AuthenticatedSessionViewControllerFactory()
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
|
||||
self.authenticationSessionService.authenticationSession(for: self.parameters.authenticationSessionParameters) { (result) in
|
||||
|
||||
switch result {
|
||||
case .success(let authenticationSessionResult):
|
||||
|
||||
switch authenticationSessionResult {
|
||||
case .authenticationNotNeeded:
|
||||
NSLog("[ReauthenticationCoordinator] No need to login again")
|
||||
self.delegate?.reauthenticationCoordinatorDidComplete(self, withAuthenticationParameters: nil)
|
||||
case .authenticationNeeded(let authenticationSession):
|
||||
if self.authenticationSessionService.hasPasswordFlow(inFlows: authenticationSession.flows) {
|
||||
self.showPasswordAuthentication(with: authenticationSession)
|
||||
} else if let authenticationFallbackURL = self.authenticationSessionService.firstUncompletedStageAuthenticationFallbackURL(for: authenticationSession) {
|
||||
|
||||
self.showFallbackAuthentication(with: authenticationFallbackURL, authenticationSession: authenticationSession)
|
||||
} else {
|
||||
self.delegate?.reauthenticationCoordinator(self, didFailWithError: AuthenticationSessionServiceError.flowNotSupported)
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
self.delegate?.reauthenticationCoordinator(self, didFailWithError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.parameters.presenter.toPresentable()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func showPasswordAuthentication(with authenticationSession: MXAuthenticationSession) {
|
||||
guard let userId = parameters.session.myUser.userId else {
|
||||
return
|
||||
}
|
||||
|
||||
let passwordViewController = self.authenticatedSessionViewControllerFactory.createPasswordViewController(title: self.parameters.title, message: self.parameters.message) { [weak self] (password) in
|
||||
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let sessionId = authenticationSession.session, let authenticationParameters = self.authenticationParametersBuilder.buildPasswordParameters(sessionId: sessionId, userId: userId, password: password) else {
|
||||
self.delegate?.reauthenticationCoordinator(self, didFailWithError: ReauthenticationCoordinatorError.failToBuildPasswordParameters)
|
||||
return
|
||||
}
|
||||
|
||||
self.delegate?.reauthenticationCoordinatorDidComplete(self, withAuthenticationParameters: authenticationParameters)
|
||||
|
||||
} onCancelled: { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.reauthenticationCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
self.presentingViewController.present(passwordViewController, animated: true)
|
||||
}
|
||||
|
||||
private func showFallbackAuthentication(with authenticationURL: URL, authenticationSession: MXAuthenticationSession) {
|
||||
|
||||
// NOTE: Prefer use a callback and the same mechanism as SSOAuthentificationSession instead of using custom WKWebView
|
||||
let reauthFallbackViewController: ReauthFallBackViewController = ReauthFallBackViewController(url: authenticationURL.absoluteString)
|
||||
reauthFallbackViewController.title = self.parameters.title
|
||||
|
||||
reauthFallbackViewController.didCancel = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.reauthenticationCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
reauthFallbackViewController.didValidate = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let sessionId = authenticationSession.session else {
|
||||
self.delegate?.reauthenticationCoordinator(self, didFailWithError: ReauthenticationCoordinatorError.failToBuildPasswordParameters)
|
||||
return
|
||||
}
|
||||
|
||||
let authenticationParameters = self.authenticationParametersBuilder.buildOAuthParameters(with: sessionId)
|
||||
self.delegate?.reauthenticationCoordinatorDidComplete(self, withAuthenticationParameters: authenticationParameters)
|
||||
}
|
||||
|
||||
let navigationController = RiotNavigationController(rootViewController: reauthFallbackViewController)
|
||||
|
||||
self.presentingViewController.present(navigationController, animated: true)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Reauthentication Reauthentication
|
||||
/*
|
||||
Copyright 2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc protocol ReauthenticationCoordinatorBridgePresenterDelegate {
|
||||
func reauthenticationCoordinatorBridgePresenterDidComplete(_ bridgePresenter: ReauthenticationCoordinatorBridgePresenter, withAuthenticationParameters authenticationParameters: [String: Any]?)
|
||||
func reauthenticationCoordinatorBridgePresenterDidCancel(_ bridgePresenter: ReauthenticationCoordinatorBridgePresenter)
|
||||
func reauthenticationCoordinatorBridgePresenter(_ bridgePresenter: ReauthenticationCoordinatorBridgePresenter, didFailWithError error: Error)
|
||||
|
||||
}
|
||||
|
||||
/// ReauthenticationCoordinatorBridgePresenter enables to start ReauthenticationCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers).
|
||||
/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
|
||||
@objcMembers
|
||||
final class ReauthenticationCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: ReauthenticationCoordinatorParameters
|
||||
private var coordinator: ReauthenticationCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: ReauthenticationCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: ReauthenticationCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(from viewController: UIViewController, animated: Bool) {
|
||||
let reauthenticationCoordinator = ReauthenticationCoordinator(parameters: self.parameters)
|
||||
reauthenticationCoordinator.delegate = self
|
||||
viewController.present(reauthenticationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
reauthenticationCoordinator.start()
|
||||
|
||||
self.coordinator = reauthenticationCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReauthenticationCoordinatorDelegate
|
||||
extension ReauthenticationCoordinatorBridgePresenter: ReauthenticationCoordinatorDelegate {
|
||||
func reauthenticationCoordinatorDidComplete(_ coordinator: ReauthenticationCoordinatorType, withAuthenticationParameters authenticationParameters: [String: Any]?) {
|
||||
self.delegate?.reauthenticationCoordinatorBridgePresenterDidComplete(self, withAuthenticationParameters: authenticationParameters)
|
||||
}
|
||||
|
||||
func reauthenticationCoordinatorDidCancel(_ coordinator: ReauthenticationCoordinatorType) {
|
||||
self.delegate?.reauthenticationCoordinatorBridgePresenterDidCancel(self)
|
||||
}
|
||||
|
||||
func reauthenticationCoordinator(_ coordinator: ReauthenticationCoordinatorType, didFailWithError error: Error) {
|
||||
self.delegate?.reauthenticationCoordinatorBridgePresenter(self, didFailWithError: error)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// ReauthenticationCoordinator input parameters
|
||||
@objcMembers
|
||||
class ReauthenticationCoordinatorParameters: NSObject {
|
||||
|
||||
/// The Matrix session
|
||||
let session: MXSession
|
||||
|
||||
/// The presenter used to show authentication screen(s)
|
||||
let presenter: Presentable
|
||||
|
||||
/// The title to use in the authentication screen if present.
|
||||
let title: String?
|
||||
|
||||
/// The message to use in the authentication screen if present.
|
||||
let message: String?
|
||||
|
||||
/// The authenticated API endpoint parameters
|
||||
let authenticationSessionParameters: AuthenticationSessionParameters
|
||||
|
||||
init(session: MXSession,
|
||||
presenter: Presentable,
|
||||
title: String?,
|
||||
message: String?,
|
||||
authenticationSessionParameters: AuthenticationSessionParameters) {
|
||||
self.session = session
|
||||
self.presenter = presenter
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.authenticationSessionParameters = authenticationSessionParameters
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Reauthentication Reauthentication
|
||||
/*
|
||||
Copyright 2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol ReauthenticationCoordinatorDelegate: class {
|
||||
func reauthenticationCoordinatorDidComplete(_ coordinator: ReauthenticationCoordinatorType, withAuthenticationParameters: [String: Any]?)
|
||||
func reauthenticationCoordinatorDidCancel(_ coordinator: ReauthenticationCoordinatorType)
|
||||
func reauthenticationCoordinator(_ coordinator: ReauthenticationCoordinatorType, didFailWithError: Error)
|
||||
}
|
||||
|
||||
/// `ReauthenticationCoordinatorType` is a protocol describing a Coordinator that handle reauthentication. It is used before calling an authenticated API.
|
||||
protocol ReauthenticationCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: ReauthenticationCoordinatorDelegate? { get }
|
||||
}
|
|
@ -56,11 +56,31 @@ final class SecretsResetCoordinator: SecretsResetCoordinatorType {
|
|||
func toPresentable() -> UIViewController {
|
||||
return self.secretsResetViewController
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func showAuthentication(with authenticationSessionParameters: AuthenticationSessionParameters) {
|
||||
|
||||
let reauthenticationCoordinatorParameters = ReauthenticationCoordinatorParameters(session: self.session,
|
||||
presenter: self.toPresentable(),
|
||||
title: nil,
|
||||
message: VectorL10n.secretsResetAuthenticationMessage,
|
||||
authenticationSessionParameters: authenticationSessionParameters)
|
||||
|
||||
let coordinator = ReauthenticationCoordinator(parameters: reauthenticationCoordinatorParameters)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
self.add(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SecretsResetViewModelCoordinatorDelegate
|
||||
extension SecretsResetCoordinator: SecretsResetViewModelCoordinatorDelegate {
|
||||
|
||||
func secretsResetViewModel(_ viewModel: SecretsResetViewModelType, needsToAuthenticateWith authenticationSessionParamaters: AuthenticationSessionParameters) {
|
||||
self.showAuthentication(with: authenticationSessionParamaters)
|
||||
}
|
||||
|
||||
func secretsResetViewModelDidResetSecrets(_ viewModel: SecretsResetViewModelType) {
|
||||
self.delegate?.secretsResetCoordinatorDidResetSecrets(self)
|
||||
}
|
||||
|
@ -69,3 +89,21 @@ extension SecretsResetCoordinator: SecretsResetViewModelCoordinatorDelegate {
|
|||
self.delegate?.secretsResetCoordinatorDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReauthenticationCoordinatorDelegate
|
||||
extension SecretsResetCoordinator: ReauthenticationCoordinatorDelegate {
|
||||
|
||||
func reauthenticationCoordinatorDidComplete(_ coordinator: ReauthenticationCoordinatorType, withAuthenticationParameters authenticationParameters: [String: Any]?) {
|
||||
|
||||
self.secretsResetViewModel.process(viewAction: .authenticationInfoEntered(authenticationParameters ?? [:]))
|
||||
}
|
||||
|
||||
func reauthenticationCoordinatorDidCancel(_ coordinator: ReauthenticationCoordinatorType) {
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
|
||||
func reauthenticationCoordinator(_ coordinator: ReauthenticationCoordinatorType, didFailWithError error: Error) {
|
||||
self.secretsResetViewModel.update(viewState: .error(error))
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,6 @@ final class SecretsResetViewController: UIViewController {
|
|||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
private var authenticatedSessionFactory: AuthenticatedSessionViewControllerFactory?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: SecretsResetViewModelType) -> SecretsResetViewController {
|
||||
|
@ -134,8 +132,6 @@ final class SecretsResetViewController: UIViewController {
|
|||
self.renderLoading()
|
||||
case .resetDone:
|
||||
self.renderLoaded()
|
||||
case .showAuthentication(authData: let authData):
|
||||
self.showAuthentication(authData: authData)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
|
@ -153,33 +149,6 @@ final class SecretsResetViewController: UIViewController {
|
|||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func showAuthentication(authData: SecretsResetAuthData) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
let authenticatedSessionFactory = authData.authenticatedSessionViewControllerFactory
|
||||
|
||||
authenticatedSessionFactory.viewController(forPath: authData.path, httpMethod: authData.httpMethod, title: nil, message: VectorL10n.secretsResetAuthenticationMessage, onViewController: { [weak self] (viewController) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.present(viewController, animated: true, completion: nil)
|
||||
}, onAuthenticated: { [weak self] (authInfo) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.viewModel.process(viewAction: .authenticationInfoEntered(authInfo))
|
||||
}, onCancelled: {
|
||||
|
||||
}, onFailure: { [weak self] (error) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.render(error: error)
|
||||
})
|
||||
|
||||
self.authenticatedSessionFactory = authenticatedSessionFactory
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ final class SecretsResetViewModel: SecretsResetViewModelType {
|
|||
|
||||
private let session: MXSession
|
||||
private let recoveryService: MXRecoveryService
|
||||
private let crossSigningService: CrossSigningService
|
||||
|
||||
// MARK: Public
|
||||
|
||||
|
@ -37,6 +38,7 @@ final class SecretsResetViewModel: SecretsResetViewModelType {
|
|||
init(session: MXSession) {
|
||||
self.session = session
|
||||
self.recoveryService = session.crypto.recoveryService
|
||||
self.crossSigningService = CrossSigningService()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
@ -47,8 +49,8 @@ final class SecretsResetViewModel: SecretsResetViewModelType {
|
|||
break
|
||||
case .reset:
|
||||
self.askAuthentication()
|
||||
case .authenticationInfoEntered(let authInfo):
|
||||
self.resetSecrets(with: authInfo)
|
||||
case .authenticationInfoEntered(let authParameters):
|
||||
self.resetSecrets(with: authParameters)
|
||||
case .cancel:
|
||||
self.coordinatorDelegate?.secretsResetViewModelDidCancel(self)
|
||||
}
|
||||
|
@ -56,18 +58,18 @@ final class SecretsResetViewModel: SecretsResetViewModelType {
|
|||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(viewState: SecretsResetViewState) {
|
||||
func update(viewState: SecretsResetViewState) {
|
||||
self.viewDelegate?.secretsResetViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
private func resetSecrets(with authInfo: [String: Any]) {
|
||||
private func resetSecrets(with authParameters: [String: Any]) {
|
||||
guard let crossSigning = self.session.crypto.crossSigning else {
|
||||
return
|
||||
}
|
||||
NSLog("[SecretsResetViewModel] resetSecrets")
|
||||
|
||||
self.update(viewState: .resetting)
|
||||
crossSigning.setup(withAuthParams: authInfo, success: { [weak self] in
|
||||
crossSigning.setup(withAuthParams: authParameters, success: { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
@ -93,12 +95,8 @@ final class SecretsResetViewModel: SecretsResetViewModelType {
|
|||
})
|
||||
}
|
||||
|
||||
// NOTE: Use a Coordinator instead of AuthenticatedSessionViewControllerFactory and delegate the presentation to SecretsResetCoordinator
|
||||
private func askAuthentication() {
|
||||
let path = "\(kMXAPIPrefixPathUnstable)/keys/device_signing/upload"
|
||||
let authenticatedSessionFactory = AuthenticatedSessionViewControllerFactory(session: self.session)
|
||||
let authData = SecretsResetAuthData(path: path, httpMethod: "POST", authenticatedSessionViewControllerFactory: authenticatedSessionFactory)
|
||||
|
||||
self.update(viewState: .showAuthentication(authData: authData))
|
||||
let authenticationSessionParameters = self.crossSigningService.setupCrossSigningAuthenticationSessionParameters()
|
||||
self.coordinatorDelegate?.secretsResetViewModel(self, needsToAuthenticateWith: authenticationSessionParameters)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ protocol SecretsResetViewModelViewDelegate: class {
|
|||
}
|
||||
|
||||
protocol SecretsResetViewModelCoordinatorDelegate: class {
|
||||
func secretsResetViewModel(_ viewModel: SecretsResetViewModelType, needsToAuthenticateWith authenticationSessionParamaters: AuthenticationSessionParameters)
|
||||
func secretsResetViewModelDidResetSecrets(_ viewModel: SecretsResetViewModelType)
|
||||
func secretsResetViewModelDidCancel(_ viewModel: SecretsResetViewModelType)
|
||||
}
|
||||
|
@ -33,5 +34,6 @@ protocol SecretsResetViewModelType {
|
|||
var viewDelegate: SecretsResetViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: SecretsResetViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func update(viewState: SecretsResetViewState)
|
||||
func process(viewAction: SecretsResetViewAction)
|
||||
}
|
||||
|
|
|
@ -18,15 +18,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
struct SecretsResetAuthData {
|
||||
let path: String
|
||||
let httpMethod: String
|
||||
let authenticatedSessionViewControllerFactory: AuthenticatedSessionViewControllerFactory
|
||||
}
|
||||
|
||||
/// SecretsResetViewController view state
|
||||
enum SecretsResetViewState {
|
||||
case showAuthentication(authData: SecretsResetAuthData)
|
||||
case resetting
|
||||
case resetDone
|
||||
case error(Error)
|
||||
|
|
|
@ -139,6 +139,7 @@ TableViewSectionsDelegate>
|
|||
@property (nonatomic, strong) SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) AuthenticatedSessionViewControllerFactory *authenticatedSessionViewControllerFactory;
|
||||
@property (nonatomic, strong) SetPinCoordinatorBridgePresenter *setPinCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -759,59 +760,42 @@ TableViewSectionsDelegate>
|
|||
- (void)setupCrossSigningWithTitle:(NSString*)title
|
||||
message:(NSString*)message
|
||||
success:(void (^)(void))success
|
||||
failure:(void (^)(NSError *error))failure
|
||||
failure:(void (^)(NSError *error))failure
|
||||
|
||||
{
|
||||
__block UIViewController *viewController;
|
||||
[self startActivityIndicator];
|
||||
|
||||
// Get credentials to set up cross-signing
|
||||
NSString *path = [NSString stringWithFormat:@"%@/keys/device_signing/upload", kMXAPIPrefixPathUnstable];
|
||||
_authenticatedSessionViewControllerFactory = [[AuthenticatedSessionViewControllerFactory alloc] initWithSession:self.mainSession];
|
||||
[_authenticatedSessionViewControllerFactory viewControllerForPath:path
|
||||
httpMethod:@"POST"
|
||||
title:title
|
||||
message:message
|
||||
onViewController:^(UIViewController * _Nonnull theViewController)
|
||||
{
|
||||
viewController = theViewController;
|
||||
[self presentViewController:viewController animated:YES completion:nil];
|
||||
|
||||
} onAuthenticated:^(NSDictionary * _Nonnull authParams) {
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
|
||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
||||
if (crossSigning)
|
||||
{
|
||||
[crossSigning setupWithAuthParams:authParams success:^{
|
||||
[self stopActivityIndicator];
|
||||
[self reloadData];
|
||||
success();
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
[self stopActivityIndicator];
|
||||
[self reloadData];
|
||||
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
failure(error);
|
||||
}];
|
||||
}
|
||||
|
||||
} onCancelled:^{
|
||||
[self stopActivityIndicator];
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
failure(nil);
|
||||
} onFailure:^(NSError * _Nonnull error) {
|
||||
|
||||
[self stopActivityIndicator];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
failure(error);
|
||||
MXWeakify(self);
|
||||
|
||||
void (^animationCompletion)(void) = ^void () {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self stopActivityIndicator];
|
||||
[self.crossSigningSetupCoordinatorBridgePresenter dismissWithAnimated:YES completion:^{}];
|
||||
self.crossSigningSetupCoordinatorBridgePresenter = nil;
|
||||
};
|
||||
|
||||
CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter = [[CrossSigningSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
|
||||
|
||||
[crossSigningSetupCoordinatorBridgePresenter presentWith:title
|
||||
message:message
|
||||
from:self
|
||||
animated:YES
|
||||
success:^{
|
||||
animationCompletion();
|
||||
[self reloadData];
|
||||
success();
|
||||
} cancel:^{
|
||||
animationCompletion();
|
||||
failure(nil);
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
animationCompletion();
|
||||
[self reloadData];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
failure(error);
|
||||
}];
|
||||
|
||||
self.crossSigningSetupCoordinatorBridgePresenter = crossSigningSetupCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
- (void)resetCrossSigning:(id)sender
|
||||
|
|
|
@ -249,6 +249,8 @@ TableViewSectionsDelegate>
|
|||
|
||||
@property (nonatomic, strong) InviteFriendsPresenter *inviteFriendsPresenter;
|
||||
|
||||
@property (nonatomic, strong) CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SettingsViewController
|
||||
|
@ -4029,58 +4031,42 @@ TableViewSectionsDelegate>
|
|||
message:(NSString*)message
|
||||
success:(void (^)(void))success
|
||||
failure:(void (^)(NSError *error))failure
|
||||
{
|
||||
__block UIViewController *viewController;
|
||||
[self startActivityIndicator];
|
||||
|
||||
// Get credentials to set up cross-signing
|
||||
NSString *path = [NSString stringWithFormat:@"%@/keys/device_signing/upload", kMXAPIPrefixPathUnstable];
|
||||
_authenticatedSessionViewControllerFactory = [[AuthenticatedSessionViewControllerFactory alloc] initWithSession:self.mainSession];
|
||||
[_authenticatedSessionViewControllerFactory viewControllerForPath:path
|
||||
httpMethod:@"POST"
|
||||
title:title
|
||||
message:message
|
||||
onViewController:^(UIViewController * _Nonnull theViewController)
|
||||
{
|
||||
viewController = theViewController;
|
||||
[self presentViewController:viewController animated:YES completion:nil];
|
||||
|
||||
} onAuthenticated:^(NSDictionary * _Nonnull authParams) {
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
|
||||
MXCrossSigning *crossSigning = self.mainSession.crypto.crossSigning;
|
||||
if (crossSigning)
|
||||
{
|
||||
[crossSigning setupWithAuthParams:authParams success:^{
|
||||
[self stopActivityIndicator];
|
||||
success();
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
[self stopActivityIndicator];
|
||||
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
failure(error);
|
||||
}];
|
||||
}
|
||||
|
||||
} onCancelled:^{
|
||||
[self stopActivityIndicator];
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
failure(nil);
|
||||
} onFailure:^(NSError * _Nonnull error) {
|
||||
|
||||
[self stopActivityIndicator];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
[viewController dismissViewControllerAnimated:NO completion:nil];
|
||||
viewController = nil;
|
||||
failure(error);
|
||||
}];
|
||||
}
|
||||
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
self.view.userInteractionEnabled = NO;
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
void (^animationCompletion)(void) = ^void () {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self stopActivityIndicator];
|
||||
self.view.userInteractionEnabled = YES;
|
||||
[self.crossSigningSetupCoordinatorBridgePresenter dismissWithAnimated:YES completion:^{}];
|
||||
self.crossSigningSetupCoordinatorBridgePresenter = nil;
|
||||
};
|
||||
|
||||
CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter = [[CrossSigningSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
|
||||
|
||||
[crossSigningSetupCoordinatorBridgePresenter presentWith:title
|
||||
message:message
|
||||
from:self
|
||||
animated:YES
|
||||
success:^{
|
||||
animationCompletion();
|
||||
success();
|
||||
} cancel:^{
|
||||
animationCompletion();
|
||||
failure(nil);
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
animationCompletion();
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
failure(error);
|
||||
}];
|
||||
|
||||
self.crossSigningSetupCoordinatorBridgePresenter = crossSigningSetupCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
#pragma mark - SingleImagePickerPresenterDelegate
|
||||
|
||||
|
|
|
@ -26,3 +26,4 @@
|
|||
#import "RoomSettingsViewController.h"
|
||||
#import "JitsiWidgetData.h"
|
||||
#import "InviteRecentTableViewCell.h"
|
||||
#import "AuthFallBackViewController.h"
|
||||
|
|
55
RiotTests/AuthenticationSessionServiceTests.swift
Normal file
55
RiotTests/AuthenticationSessionServiceTests.swift
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable import Riot
|
||||
|
||||
class AuthenticationSessionServiceTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
// MARK: - Tests
|
||||
|
||||
func testGetFirstUncompletedStage() {
|
||||
|
||||
let flow1 = MXLoginFlow()
|
||||
flow1.stages = ["example.type.foo", "example.type.bar"]
|
||||
|
||||
let flow2 = MXLoginFlow()
|
||||
flow2.stages = ["example.type.foo", "example.type.baz"]
|
||||
|
||||
let completedStages = ["example.type.foo"]
|
||||
|
||||
let authenticationSession = MXAuthenticationSession()
|
||||
authenticationSession.completed = completedStages
|
||||
authenticationSession.flows = [flow1, flow2]
|
||||
|
||||
let mxSession = MXSession()
|
||||
let authenticationSessionService = AuthenticationSessionService(session: mxSession)
|
||||
|
||||
let firstUncompletedStage = authenticationSessionService.firstUncompletedFlowIdentifier(in: authenticationSession)
|
||||
|
||||
XCTAssertNotNil(firstUncompletedStage)
|
||||
XCTAssertEqual(firstUncompletedStage, "example.type.bar")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue