Merge pull request #3997 from vector-im/element_3995

Handle User-Interactive Authentication fallback
This commit is contained in:
SBiOSoftWhare 2021-02-03 17:20:00 +01:00 committed by GitHub
commit 7970b3fd97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1339 additions and 405 deletions

View file

@ -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).

View file

@ -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 */,

View file

@ -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 %@";

View file

@ -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")
}

View file

@ -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
]
}
}

View file

@ -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
]
}
}

View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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")
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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()
}
}

View file

@ -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 }
}

View file

@ -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

View file

@ -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() {

View file

@ -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?()
}
}

View 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)
}
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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 }
}

View file

@ -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)
}
}

View file

@ -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

View file

@ -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)
}
}

View file

@ -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)
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -26,3 +26,4 @@
#import "RoomSettingsViewController.h"
#import "JitsiWidgetData.h"
#import "InviteRecentTableViewCell.h"
#import "AuthFallBackViewController.h"

View 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")
}
}