Rename OIDC to OAuth. (#5525)

* Rename OIDC to OAuth.

* Update the enterprise submodule.
This commit is contained in:
Doug
2026-05-05 14:07:06 +01:00
committed by GitHub
parent 50f4022b7c
commit fe6c62b60f
57 changed files with 402 additions and 400 deletions

View File

@@ -488,7 +488,6 @@
513AF15E0E84711B80D04B1B /* ReportRoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3E9684DCE6B66BD0B5DF67 /* ReportRoomScreenViewModelTests.swift */; }; 513AF15E0E84711B80D04B1B /* ReportRoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3E9684DCE6B66BD0B5DF67 /* ReportRoomScreenViewModelTests.swift */; };
51B3B19FA5F91B455C807BA7 /* RoomPollsHistoryScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E964AF2DFEB31E2B799999F /* RoomPollsHistoryScreenModels.swift */; }; 51B3B19FA5F91B455C807BA7 /* RoomPollsHistoryScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E964AF2DFEB31E2B799999F /* RoomPollsHistoryScreenModels.swift */; };
522269133E6F65F68482F4F4 /* RemotePreferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181CF280BC8E3F335AFCB4B8 /* RemotePreferenceTests.swift */; }; 522269133E6F65F68482F4F4 /* RemotePreferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181CF280BC8E3F335AFCB4B8 /* RemotePreferenceTests.swift */; };
523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */; };
52473A4D7B1FBD4CD1E770C8 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; }; 52473A4D7B1FBD4CD1E770C8 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; };
530C2238E40F71223327FC95 /* MockTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA8082E26C77A2C587B34B3 /* MockTimelineController.swift */; }; 530C2238E40F71223327FC95 /* MockTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA8082E26C77A2C587B34B3 /* MockTimelineController.swift */; };
5341D48F833E3E30F16FA2A3 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2910422CB628D3B2BBE47449 /* SeparatorRoomTimelineView.swift */; }; 5341D48F833E3E30F16FA2A3 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2910422CB628D3B2BBE47449 /* SeparatorRoomTimelineView.swift */; };
@@ -634,6 +633,7 @@
6A5FDF9306CBD62C7EDDB552 /* CLLocationManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C723327DC4A3093CD9675B27 /* CLLocationManagerMock.swift */; }; 6A5FDF9306CBD62C7EDDB552 /* CLLocationManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C723327DC4A3093CD9675B27 /* CLLocationManagerMock.swift */; };
6A64546ABE648ED9E6DBB459 /* RemoteSettingsHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D186A6DB8FAC5C9D0E4D61 /* RemoteSettingsHook.swift */; }; 6A64546ABE648ED9E6DBB459 /* RemoteSettingsHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D186A6DB8FAC5C9D0E4D61 /* RemoteSettingsHook.swift */; };
6AB306367E56A6F6DFA0E2FF /* RoomSummaryProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46E441BA50705E6CEC89FE0 /* RoomSummaryProviderTests.swift */; }; 6AB306367E56A6F6DFA0E2FF /* RoomSummaryProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46E441BA50705E6CEC89FE0 /* RoomSummaryProviderTests.swift */; };
6AC798F52571BE495E6AA1CE /* OAuthAccountSettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21819C86A022D6ADBA7A7A15 /* OAuthAccountSettingsPresenter.swift */; };
6AD722DD92E465E56D2885AB /* BugReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA919F521E9F0EE3638AFC85 /* BugReportScreen.swift */; }; 6AD722DD92E465E56D2885AB /* BugReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA919F521E9F0EE3638AFC85 /* BugReportScreen.swift */; };
6AEB650311F694A5702255C9 /* UserProfileScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B4932E4EFBC8FAC10972CD /* UserProfileScreenCoordinator.swift */; }; 6AEB650311F694A5702255C9 /* UserProfileScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B4932E4EFBC8FAC10972CD /* UserProfileScreenCoordinator.swift */; };
6B31508C6334C617360C2EAB /* RoomMemberDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */; }; 6B31508C6334C617360C2EAB /* RoomMemberDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */; };
@@ -680,7 +680,6 @@
726AA74DF4E5EFCEBD78CE3F /* RoomMembersFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A70B03471F6027C90EE868C /* RoomMembersFlowCoordinator.swift */; }; 726AA74DF4E5EFCEBD78CE3F /* RoomMembersFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A70B03471F6027C90EE868C /* RoomMembersFlowCoordinator.swift */; };
72D2298DE695A6797CDA1A2A /* SpaceScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B223FA339BF53085328DEE /* SpaceScreenViewModelTests.swift */; }; 72D2298DE695A6797CDA1A2A /* SpaceScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B223FA339BF53085328DEE /* SpaceScreenViewModelTests.swift */; };
733E2B19AB1FDA3B93293A28 /* AppLockSetupPINScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */; }; 733E2B19AB1FDA3B93293A28 /* AppLockSetupPINScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */; };
7366E5783D1871D42CF99D34 /* OIDCConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D354D4232DED9649FD0FF4 /* OIDCConfiguration.swift */; };
738288EAEE235CAC0893AB9E /* ThreadTimelineScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C9ACDD96F36510C1FC0836B /* ThreadTimelineScreenViewModel.swift */; }; 738288EAEE235CAC0893AB9E /* ThreadTimelineScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C9ACDD96F36510C1FC0836B /* ThreadTimelineScreenViewModel.swift */; };
73DBE886625AF56FF08D7F76 /* CoordinateAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA74F57B0DA3B9A9DD51F691 /* CoordinateAnimator.swift */; }; 73DBE886625AF56FF08D7F76 /* CoordinateAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA74F57B0DA3B9A9DD51F691 /* CoordinateAnimator.swift */; };
73F33E9776B7A50B65A031D2 /* AppLockSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BA67B3E4EF9D29D14A78CE /* AppLockSettingsScreenViewModelTests.swift */; }; 73F33E9776B7A50B65A031D2 /* AppLockSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BA67B3E4EF9D29D14A78CE /* AppLockSettingsScreenViewModelTests.swift */; };
@@ -697,7 +696,6 @@
756EA0D663261889EF64E6D4 /* VoiceMessageRecordingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9CBF577B9711CFBB4FA40D /* VoiceMessageRecordingView.swift */; }; 756EA0D663261889EF64E6D4 /* VoiceMessageRecordingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9CBF577B9711CFBB4FA40D /* VoiceMessageRecordingView.swift */; };
7573D682F089205F7F1D96CF /* SessionDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C2067FF58B4996323EB40C /* SessionDirectories.swift */; }; 7573D682F089205F7F1D96CF /* SessionDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C2067FF58B4996323EB40C /* SessionDirectories.swift */; };
757862045774A0F458357E19 /* RoomThreadListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544B9262E1BC6F489C03FFFA /* RoomThreadListScreen.swift */; }; 757862045774A0F458357E19 /* RoomThreadListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544B9262E1BC6F489C03FFFA /* RoomThreadListScreen.swift */; };
75AD7C09BD604A68E2FAA1D9 /* OIDCConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D354D4232DED9649FD0FF4 /* OIDCConfiguration.swift */; };
75ED4B73983228BB6922CE3C /* KnockRequestsListScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5C217DD0749EC709EED028 /* KnockRequestsListScreenViewModelProtocol.swift */; }; 75ED4B73983228BB6922CE3C /* KnockRequestsListScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5C217DD0749EC709EED028 /* KnockRequestsListScreenViewModelProtocol.swift */; };
761EA50B2619307AB30891B8 /* PhishingDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB07F03461023BC39C730922 /* PhishingDetector.swift */; }; 761EA50B2619307AB30891B8 /* PhishingDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB07F03461023BC39C730922 /* PhishingDetector.swift */; };
7624B61D0A3EFEC69C666609 /* SpaceAddRoomsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB74D407F0E413E7593B369 /* SpaceAddRoomsScreenViewModelTests.swift */; }; 7624B61D0A3EFEC69C666609 /* SpaceAddRoomsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB74D407F0E413E7593B369 /* SpaceAddRoomsScreenViewModelTests.swift */; };
@@ -797,6 +795,7 @@
859E2CA2EDF343BD24DE52EB /* RoomDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */; }; 859E2CA2EDF343BD24DE52EB /* RoomDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */; };
85BD82E144AB99518A57DDEC /* preview_avatar_room.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 12FD5280AF55AB7F50F8E47D /* preview_avatar_room.jpg */; }; 85BD82E144AB99518A57DDEC /* preview_avatar_room.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 12FD5280AF55AB7F50F8E47D /* preview_avatar_room.jpg */; };
85F89F3F320F4FADCFFFE68B /* ServerSelectionScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */; }; 85F89F3F320F4FADCFFFE68B /* ServerSelectionScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */; };
86320FDBD2F12A0225B1BCBE /* OAuthConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002DE5E9625122F37DE5F020 /* OAuthConfiguration.swift */; };
864C0D3A4077BF433DBC691F /* PollRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5281C5CDC4A712265A0B5FBF /* PollRoomTimelineItem.swift */; }; 864C0D3A4077BF433DBC691F /* PollRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5281C5CDC4A712265A0B5FBF /* PollRoomTimelineItem.swift */; };
8658F5034EAD7357CE7F9AC7 /* MatrixUserShareLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */; }; 8658F5034EAD7357CE7F9AC7 /* MatrixUserShareLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */; };
865DD5CA474C6AE6C2BC008E /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */; }; 865DD5CA474C6AE6C2BC008E /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */; };
@@ -929,7 +928,6 @@
99ED42B8F8D6BFB1DBCF4C45 /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = D661CAB418C075A94306A792 /* AnalyticsEvents */; }; 99ED42B8F8D6BFB1DBCF4C45 /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = D661CAB418C075A94306A792 /* AnalyticsEvents */; };
9A0326D2375075871D2AB537 /* ResolveVerifiedUserSendFailureScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 574CB70E82D7EAEA538E4135 /* ResolveVerifiedUserSendFailureScreenViewModel.swift */; }; 9A0326D2375075871D2AB537 /* ResolveVerifiedUserSendFailureScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 574CB70E82D7EAEA538E4135 /* ResolveVerifiedUserSendFailureScreenViewModel.swift */; };
9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; }; 9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; };
9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */; };
9A8E6FCD86B89970EC72EFD8 /* BugReportServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7FC9580CABF797A2E6213A /* BugReportServiceMock.swift */; }; 9A8E6FCD86B89970EC72EFD8 /* BugReportServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7FC9580CABF797A2E6213A /* BugReportServiceMock.swift */; };
9AC47275B8E1EB0976BA7A80 /* MapTilerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1F2AAA3F0F2B72D2FFE4D0 /* MapTilerConfiguration.swift */; }; 9AC47275B8E1EB0976BA7A80 /* MapTilerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1F2AAA3F0F2B72D2FFE4D0 /* MapTilerConfiguration.swift */; };
9AC5F8142413862A9E3A2D98 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = A7CA6F33C553805035C3B114 /* DeviceKit */; }; 9AC5F8142413862A9E3A2D98 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = A7CA6F33C553805035C3B114 /* DeviceKit */; };
@@ -1005,7 +1003,6 @@
A5B455D1A6DADF7476F7B417 /* EmojiProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCCE3D12B0A9C6E559B5B5A /* EmojiProviderProtocol.swift */; }; A5B455D1A6DADF7476F7B417 /* EmojiProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCCE3D12B0A9C6E559B5B5A /* EmojiProviderProtocol.swift */; };
A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9780389F8A53E4D26E23DD03 /* LoginScreenViewModelProtocol.swift */; }; A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9780389F8A53E4D26E23DD03 /* LoginScreenViewModelProtocol.swift */; };
A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; }; A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; };
A5F50F36E56E5D3C241E2BE3 /* OIDCConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D354D4232DED9649FD0FF4 /* OIDCConfiguration.swift */; };
A5FD8284744E2FECFC842FC1 /* TraceLogPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7149BDDE47F8AD104E644E2 /* TraceLogPack.swift */; }; A5FD8284744E2FECFC842FC1 /* TraceLogPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7149BDDE47F8AD104E644E2 /* TraceLogPack.swift */; };
A64B52D9F73F9A6B95AF24FE /* UserDetailsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CD503F5E0938FE53C7C6E7 /* UserDetailsEditScreenCoordinator.swift */; }; A64B52D9F73F9A6B95AF24FE /* UserDetailsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CD503F5E0938FE53C7C6E7 /* UserDetailsEditScreenCoordinator.swift */; };
A6B83EB78F025D21B6EBA90C /* CompoundIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044E501B8331B339874D1B96 /* CompoundIcon.swift */; }; A6B83EB78F025D21B6EBA90C /* CompoundIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044E501B8331B339874D1B96 /* CompoundIcon.swift */; };
@@ -1171,6 +1168,7 @@
C4D2BCAA54E2C62B94B24AF4 /* InviteUsersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2E9B841EE4878283ECDB554 /* InviteUsersScreen.swift */; }; C4D2BCAA54E2C62B94B24AF4 /* InviteUsersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2E9B841EE4878283ECDB554 /* InviteUsersScreen.swift */; };
C4E0D03DF88242697545A9B7 /* UserIndicatorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */; }; C4E0D03DF88242697545A9B7 /* UserIndicatorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */; };
C4FE0E11A907C8999F92D5A8 /* TimelineStartRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */; }; C4FE0E11A907C8999F92D5A8 /* TimelineStartRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */; };
C525F6C2892CB0640E776B3D /* OAuthConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002DE5E9625122F37DE5F020 /* OAuthConfiguration.swift */; };
C55A44C99F64A479ABA85B46 /* RoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */; }; C55A44C99F64A479ABA85B46 /* RoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */; };
C5627BCC3EBBB96A943B6D93 /* RestorationTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7978C9EFBDD7DE39BD86726 /* RestorationTokenTests.swift */; }; C5627BCC3EBBB96A943B6D93 /* RestorationTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7978C9EFBDD7DE39BD86726 /* RestorationTokenTests.swift */; };
C58E305C380D3ADDF7912180 /* StickerRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818695BED971753243FEF897 /* StickerRoomTimelineItem.swift */; }; C58E305C380D3ADDF7912180 /* StickerRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818695BED971753243FEF897 /* StickerRoomTimelineItem.swift */; };
@@ -1344,6 +1342,7 @@
E323A54F317604BDD6968D79 /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; }; E323A54F317604BDD6968D79 /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; };
E3291AD16D7A5CB14781819C /* UserNotificationCenterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D8149FDDA0315CDC553B4B /* UserNotificationCenterProtocol.swift */; }; E3291AD16D7A5CB14781819C /* UserNotificationCenterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D8149FDDA0315CDC553B4B /* UserNotificationCenterProtocol.swift */; };
E32A18802EB37EEE3EF7B965 /* GlobalSearchScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B788615712FED326F73D3F83 /* GlobalSearchScreenViewModelProtocol.swift */; }; E32A18802EB37EEE3EF7B965 /* GlobalSearchScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B788615712FED326F73D3F83 /* GlobalSearchScreenViewModelProtocol.swift */; };
E32E71A1AF5E5E69E8363B26 /* OAuthAuthenticationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA11EF88B2848B12FA6FA36 /* OAuthAuthenticationPresenter.swift */; };
E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */; }; E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */; };
E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */; }; E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */; };
E3E1E255DC8CB34BD8573E0D /* UserIndicatorControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */; }; E3E1E255DC8CB34BD8573E0D /* UserIndicatorControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */; };
@@ -1453,6 +1452,7 @@
F3F38062C6CA21CF403C5C90 /* AudioConverterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757B1BE23DF8AA239937243 /* AudioConverterProtocol.swift */; }; F3F38062C6CA21CF403C5C90 /* AudioConverterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757B1BE23DF8AA239937243 /* AudioConverterProtocol.swift */; };
F3F9D61C53C348043D3D6F51 /* EncryptionResetScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 811E8BF34E931D51552C9C13 /* EncryptionResetScreen.swift */; }; F3F9D61C53C348043D3D6F51 /* EncryptionResetScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 811E8BF34E931D51552C9C13 /* EncryptionResetScreen.swift */; };
F40B097470D3110DFDB1FAAA /* LegalInformationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47873756E45B46683D97DC32 /* LegalInformationScreenModels.swift */; }; F40B097470D3110DFDB1FAAA /* LegalInformationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47873756E45B46683D97DC32 /* LegalInformationScreenModels.swift */; };
F43564003B017DB148DB1503 /* OAuthConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002DE5E9625122F37DE5F020 /* OAuthConfiguration.swift */; };
F4582042AA4225CC1E4B8A1E /* landscape_test_video.mov in Resources */ = {isa = PBXBuildFile; fileRef = 78BBDF7A05CF53B5CDC13682 /* landscape_test_video.mov */; }; F4582042AA4225CC1E4B8A1E /* landscape_test_video.mov in Resources */ = {isa = PBXBuildFile; fileRef = 78BBDF7A05CF53B5CDC13682 /* landscape_test_video.mov */; };
F4996C82A4B3A5FF0C8EDD03 /* RoomListFilterModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E06AAD6D9D3F5833E7A5A2F9 /* RoomListFilterModels.swift */; }; F4996C82A4B3A5FF0C8EDD03 /* RoomListFilterModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E06AAD6D9D3F5833E7A5A2F9 /* RoomListFilterModels.swift */; };
F4C005F006FC3657B9F0A31D /* BugReportHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25586C0ADB814FEE9897DCAA /* BugReportHook.swift */; }; F4C005F006FC3657B9F0A31D /* BugReportHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25586C0ADB814FEE9897DCAA /* BugReportHook.swift */; };
@@ -1613,6 +1613,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
002399C6CB875C4EBB01CBC0 /* MediaEventsTimelineScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaEventsTimelineScreen.swift; sourceTree = "<group>"; }; 002399C6CB875C4EBB01CBC0 /* MediaEventsTimelineScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaEventsTimelineScreen.swift; sourceTree = "<group>"; };
00245D40CD90FD71D6A05239 /* EmojiPickerScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreen.swift; sourceTree = "<group>"; }; 00245D40CD90FD71D6A05239 /* EmojiPickerScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreen.swift; sourceTree = "<group>"; };
002DE5E9625122F37DE5F020 /* OAuthConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthConfiguration.swift; sourceTree = "<group>"; };
007C16779FDCF10DA4F1A510 /* LinkNewDeviceService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceService.swift; sourceTree = "<group>"; }; 007C16779FDCF10DA4F1A510 /* LinkNewDeviceService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceService.swift; sourceTree = "<group>"; };
008D864B3F51B41DF483B860 /* RoomThreadListScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomThreadListScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 008D864B3F51B41DF483B860 /* RoomThreadListScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomThreadListScreenViewModelProtocol.swift; sourceTree = "<group>"; };
00AFC5F08734C2EA4EE79C59 /* IdentityConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreen.swift; sourceTree = "<group>"; }; 00AFC5F08734C2EA4EE79C59 /* IdentityConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreen.swift; sourceTree = "<group>"; };
@@ -1814,6 +1815,7 @@
2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; }; 2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; };
216F0DDC98F2A2C162D09C28 /* FileRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItemContent.swift; sourceTree = "<group>"; }; 216F0DDC98F2A2C162D09C28 /* FileRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItemContent.swift; sourceTree = "<group>"; };
2178B951602AA921A5FD9DC8 /* MediaEventsTimelineFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaEventsTimelineFlowCoordinator.swift; sourceTree = "<group>"; }; 2178B951602AA921A5FD9DC8 /* MediaEventsTimelineFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaEventsTimelineFlowCoordinator.swift; sourceTree = "<group>"; };
21819C86A022D6ADBA7A7A15 /* OAuthAccountSettingsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAccountSettingsPresenter.swift; sourceTree = "<group>"; };
218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineItemProtocol.swift; sourceTree = "<group>"; }; 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineItemProtocol.swift; sourceTree = "<group>"; };
21BA866267F84BF4350B0CB7 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; }; 21BA866267F84BF4350B0CB7 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
21DD8599815136EFF5B73F38 /* UserFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserFlowTests.swift; sourceTree = "<group>"; }; 21DD8599815136EFF5B73F38 /* UserFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserFlowTests.swift; sourceTree = "<group>"; };
@@ -2438,7 +2440,6 @@
91CF6F7D08228D16BA69B63B /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = "<group>"; }; 91CF6F7D08228D16BA69B63B /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
91FFE1F410969ECB23FE9BB2 /* TimelineItemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMenu.swift; sourceTree = "<group>"; }; 91FFE1F410969ECB23FE9BB2 /* TimelineItemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMenu.swift; sourceTree = "<group>"; };
922E498EB74CF6F5CC236F81 /* AdvancedSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenModels.swift; sourceTree = "<group>"; }; 922E498EB74CF6F5CC236F81 /* AdvancedSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenModels.swift; sourceTree = "<group>"; };
92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCAuthenticationPresenter.swift; sourceTree = "<group>"; };
92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScannerView.swift; sourceTree = "<group>"; }; 92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScannerView.swift; sourceTree = "<group>"; };
9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; }; 9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; };
933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaFrame.swift; sourceTree = "<group>"; }; 933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaFrame.swift; sourceTree = "<group>"; };
@@ -2602,6 +2603,7 @@
AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancellableTask.swift; sourceTree = "<group>"; }; AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancellableTask.swift; sourceTree = "<group>"; };
AE5DDBEBBA17973ED4638823 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; AE5DDBEBBA17973ED4638823 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
AE739A6836E86E3780748477 /* TimelineItemBubbleBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbleBackground.swift; sourceTree = "<group>"; }; AE739A6836E86E3780748477 /* TimelineItemBubbleBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbleBackground.swift; sourceTree = "<group>"; };
AEA11EF88B2848B12FA6FA36 /* OAuthAuthenticationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAuthenticationPresenter.swift; sourceTree = "<group>"; };
AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFNumberedListView.swift; sourceTree = "<group>"; }; AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFNumberedListView.swift; sourceTree = "<group>"; };
AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilderTests.swift; sourceTree = "<group>"; }; AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilderTests.swift; sourceTree = "<group>"; };
AEF2C15634499348A512A93A /* ChatsSpaceFiltersScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsSpaceFiltersScreenModels.swift; sourceTree = "<group>"; }; AEF2C15634499348A512A93A /* ChatsSpaceFiltersScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsSpaceFiltersScreenModels.swift; sourceTree = "<group>"; };
@@ -2713,7 +2715,6 @@
C142248014E08E885E323E56 /* Avatars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Avatars.swift; sourceTree = "<group>"; }; C142248014E08E885E323E56 /* Avatars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Avatars.swift; sourceTree = "<group>"; };
C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutDirection.swift; sourceTree = "<group>"; }; C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutDirection.swift; sourceTree = "<group>"; };
C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationModeProxy.swift; sourceTree = "<group>"; }; C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationModeProxy.swift; sourceTree = "<group>"; };
C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCAccountSettingsPresenter.swift; sourceTree = "<group>"; };
C1FA515B3B0D61EF1E907D2D /* BadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeView.swift; sourceTree = "<group>"; }; C1FA515B3B0D61EF1E907D2D /* BadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeView.swift; sourceTree = "<group>"; };
C258C9C815272911A5B132C3 /* FormattedBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedBodyText.swift; sourceTree = "<group>"; }; C258C9C815272911A5B132C3 /* FormattedBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedBodyText.swift; sourceTree = "<group>"; };
C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenModels.swift; sourceTree = "<group>"; }; C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenModels.swift; sourceTree = "<group>"; };
@@ -2924,7 +2925,6 @@
E8495F37D6245AD0CFA1F60B /* AppLockTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockTests.swift; sourceTree = "<group>"; }; E8495F37D6245AD0CFA1F60B /* AppLockTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockTests.swift; sourceTree = "<group>"; };
E8A1F98AE670377B20679FF5 /* MediaPlayerProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProvider.swift; sourceTree = "<group>"; }; E8A1F98AE670377B20679FF5 /* MediaPlayerProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProvider.swift; sourceTree = "<group>"; };
E8AE4B3273BA189FDCD4055C /* UserIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicator.swift; sourceTree = "<group>"; }; E8AE4B3273BA189FDCD4055C /* UserIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicator.swift; sourceTree = "<group>"; };
E8D354D4232DED9649FD0FF4 /* OIDCConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCConfiguration.swift; sourceTree = "<group>"; };
E944F717FC10A428D027074D /* RoomPowerLevelsProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPowerLevelsProxyMock.swift; sourceTree = "<group>"; }; E944F717FC10A428D027074D /* RoomPowerLevelsProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPowerLevelsProxyMock.swift; sourceTree = "<group>"; };
E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFixtures.swift; sourceTree = "<group>"; }; E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFixtures.swift; sourceTree = "<group>"; };
E992D7B8BE54B2AB454613AF /* XCUIElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = "<group>"; }; E992D7B8BE54B2AB454613AF /* XCUIElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = "<group>"; };
@@ -6519,7 +6519,7 @@
0A2074C0449B83D5858BD2D7 /* FrequentlyUsedEmoji.swift */, 0A2074C0449B83D5858BD2D7 /* FrequentlyUsedEmoji.swift */,
DA2FEFA393FC7D2870263012 /* LiveLocationSession.swift */, DA2FEFA393FC7D2870263012 /* LiveLocationSession.swift */,
8A1F2AAA3F0F2B72D2FFE4D0 /* MapTilerConfiguration.swift */, 8A1F2AAA3F0F2B72D2FFE4D0 /* MapTilerConfiguration.swift */,
E8D354D4232DED9649FD0FF4 /* OIDCConfiguration.swift */, 002DE5E9625122F37DE5F020 /* OAuthConfiguration.swift */,
8FC598338E7CF41107293AB5 /* RageshakeConfiguration.swift */, 8FC598338E7CF41107293AB5 /* RageshakeConfiguration.swift */,
69A05E85E4872C3221C5C287 /* RemotePreference.swift */, 69A05E85E4872C3221C5C287 /* RemotePreference.swift */,
A5DA892E8643240C7BC41900 /* RoomListActivityVisibility.swift */, A5DA892E8643240C7BC41900 /* RoomListActivityVisibility.swift */,
@@ -6805,7 +6805,7 @@
E74CD7681375AD2EAA34D66B /* Authentication */ = { E74CD7681375AD2EAA34D66B /* Authentication */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */, AEA11EF88B2848B12FA6FA36 /* OAuthAuthenticationPresenter.swift */,
9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */, 9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */,
90F48FEF84016ED42A94BA24 /* LoginScreen */, 90F48FEF84016ED42A94BA24 /* LoginScreen */,
BA1938A75D8C780F694CEB62 /* ServerConfirmationScreen */, BA1938A75D8C780F694CEB62 /* ServerConfirmationScreen */,
@@ -6839,7 +6839,7 @@
EB5B1119B5AD79297F1D49EB /* AccountSettings */ = { EB5B1119B5AD79297F1D49EB /* AccountSettings */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */, 21819C86A022D6ADBA7A7A15 /* OAuthAccountSettingsPresenter.swift */,
); );
path = AccountSettings; path = AccountSettings;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -7804,7 +7804,7 @@
5415236FA271AF7885D4995E /* NotificationItemProxy.swift in Sources */, 5415236FA271AF7885D4995E /* NotificationItemProxy.swift in Sources */,
CBBBE597BE74A2DF68DE2209 /* NotificationItemProxyProtocol.swift in Sources */, CBBBE597BE74A2DF68DE2209 /* NotificationItemProxyProtocol.swift in Sources */,
B14BC354E56616B6B7D9A3D7 /* NotificationServiceExtension.swift in Sources */, B14BC354E56616B6B7D9A3D7 /* NotificationServiceExtension.swift in Sources */,
A5F50F36E56E5D3C241E2BE3 /* OIDCConfiguration.swift in Sources */, 86320FDBD2F12A0225B1BCBE /* OAuthConfiguration.swift in Sources */,
761EA50B2619307AB30891B8 /* PhishingDetector.swift in Sources */, 761EA50B2619307AB30891B8 /* PhishingDetector.swift in Sources */,
5DFC2A889D3B39DD47AC63A8 /* PillUtilities.swift in Sources */, 5DFC2A889D3B39DD47AC63A8 /* PillUtilities.swift in Sources */,
55CDD3968D95D1A820B5491E /* PlaceholderAvatarImage.swift in Sources */, 55CDD3968D95D1A820B5491E /* PlaceholderAvatarImage.swift in Sources */,
@@ -8020,7 +8020,7 @@
0638CBDE3098B1C3F23AFCFA /* MXLog.swift in Sources */, 0638CBDE3098B1C3F23AFCFA /* MXLog.swift in Sources */,
074F741578307EF0179EE47C /* MapTilerConfiguration.swift in Sources */, 074F741578307EF0179EE47C /* MapTilerConfiguration.swift in Sources */,
1A3783005E6945F8583AF997 /* NSItemProvider.swift in Sources */, 1A3783005E6945F8583AF997 /* NSItemProvider.swift in Sources */,
75AD7C09BD604A68E2FAA1D9 /* OIDCConfiguration.swift in Sources */, F43564003B017DB148DB1503 /* OAuthConfiguration.swift in Sources */,
BE8E5985771DF9137C6CE89A /* ProcessInfo.swift in Sources */, BE8E5985771DF9137C6CE89A /* ProcessInfo.swift in Sources */,
58F357A9D130A654ABCB1638 /* RageshakeConfiguration.swift in Sources */, 58F357A9D130A654ABCB1638 /* RageshakeConfiguration.swift in Sources */,
24C32D7EF94ECF9081638DF6 /* RemotePreference.swift in Sources */, 24C32D7EF94ECF9081638DF6 /* RemotePreference.swift in Sources */,
@@ -8602,9 +8602,9 @@
D12F440F7973F1489F61389D /* NotificationSettingsScreenModels.swift in Sources */, D12F440F7973F1489F61389D /* NotificationSettingsScreenModels.swift in Sources */,
7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */, 7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */,
CBD2ABE4C1A47ECD99E1488E /* NotificationSettingsScreenViewModelProtocol.swift in Sources */, CBD2ABE4C1A47ECD99E1488E /* NotificationSettingsScreenViewModelProtocol.swift in Sources */,
523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */, 6AC798F52571BE495E6AA1CE /* OAuthAccountSettingsPresenter.swift in Sources */,
9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */, E32E71A1AF5E5E69E8363B26 /* OAuthAuthenticationPresenter.swift in Sources */,
7366E5783D1871D42CF99D34 /* OIDCConfiguration.swift in Sources */, C525F6C2892CB0640E776B3D /* OAuthConfiguration.swift in Sources */,
FD573B5D665824EB79EABF06 /* Observable.swift in Sources */, FD573B5D665824EB79EABF06 /* Observable.swift in Sources */,
11A6B8E3CBDBF0A4107FF4CE /* OnboardingFlowCoordinator.swift in Sources */, 11A6B8E3CBDBF0A4107FF4CE /* OnboardingFlowCoordinator.swift in Sources */,
3CE4C5071B6D2576E2473989 /* OrderedSet.swift in Sources */, 3CE4C5071B6D2576E2473989 /* OrderedSet.swift in Sources */,

View File

@@ -276,11 +276,11 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
case .accountProvisioningLink: case .accountProvisioningLink:
handleAppRoute(route, handleAppRoute(route,
windowType: windowType) windowType: windowType)
case .oidcCallback(let url): case .oAuthCallback(let url):
if stateMachine.state == .softLogout { if stateMachine.state == .softLogout {
softLogoutCoordinator?.handleOIDCRedirectURL(url) softLogoutCoordinator?.handleOAuthCallbackURL(url)
} else { } else {
authenticationFlowCoordinator?.handleOIDCRedirectURL(url) authenticationFlowCoordinator?.handleOAuthCallbackURL(url)
} }
case .userProfile(let userID): case .userProfile(let userID):
if isExternalURL { if isExternalURL {

View File

@@ -14,9 +14,9 @@ import MatrixRustSDK
enum AppRoute: Hashable { enum AppRoute: Hashable {
/// An account provisioning link generated externally. /// An account provisioning link generated externally.
case accountProvisioningLink(AccountProvisioningParameters) case accountProvisioningLink(AccountProvisioningParameters)
/// An external callback used to complete login with OIDC. This is only used when authentication /// An external callback used to complete login with OAuth. This is only used when authentication
/// requires an external app so cannot be done within the built in web authentication session. /// requires an external app so cannot be handled directly by the web authentication session.
case oidcCallback(url: URL) case oAuthCallback(url: URL)
/// The app's home screen. /// The app's home screen.
case roomList case roomList
@@ -61,7 +61,7 @@ enum AppRoute: Hashable {
var isAuthenticationRoute: Bool { var isAuthenticationRoute: Bool {
switch self { switch self {
case .accountProvisioningLink: true case .accountProvisioningLink: true
case .oidcCallback: true case .oAuthCallback: true
default: false default: false
} }
} }
@@ -87,7 +87,7 @@ struct AppRouteURLParser {
MatrixPermalinkParser(), MatrixPermalinkParser(),
ElementWebURLParser(domains: appSettings.elementWebHosts), ElementWebURLParser(domains: appSettings.elementWebHosts),
AccountProvisioningURLParser(domain: appSettings.accountProvisioningHost), AccountProvisioningURLParser(domain: appSettings.accountProvisioningHost),
OIDCCallbackURLParser(redirectURL: appSettings.oidcRedirectURL) OAuthCallbackURLParser(redirectURL: appSettings.oAuthRedirectURL)
] ]
} }
@@ -204,12 +204,12 @@ private struct AccountProvisioningURLParser: URLParser {
} }
} }
/// The parser for the OIDC callback URL. This always returns a `.oidcCallback`. /// The parser for the OAuth callback URL. This always returns an `.oAuthCallback`.
struct OIDCCallbackURLParser: URLParser { struct OAuthCallbackURLParser: URLParser {
let redirectURL: URL let redirectURL: URL
func route(from url: URL) -> AppRoute? { func route(from url: URL) -> AppRoute? {
guard url.absoluteString.starts(with: redirectURL.absoluteString) else { return nil } guard url.absoluteString.starts(with: redirectURL.absoluteString) else { return nil }
return .oidcCallback(url: url) return .oAuthCallback(url: url)
} }
} }

View File

@@ -135,7 +135,7 @@ final class AppSettings {
allowOtherAccountProviders: Bool, allowOtherAccountProviders: Bool,
hideBrandChrome: Bool, hideBrandChrome: Bool,
pushGatewayBaseURL: URL, pushGatewayBaseURL: URL,
oidcRedirectURL: URL, oAuthRedirectURL: URL,
websiteURL: URL, websiteURL: URL,
logoURL: URL, logoURL: URL,
copyrightURL: URL, copyrightURL: URL,
@@ -155,7 +155,7 @@ final class AppSettings {
self.allowOtherAccountProviders = allowOtherAccountProviders self.allowOtherAccountProviders = allowOtherAccountProviders
self.hideBrandChrome = hideBrandChrome self.hideBrandChrome = hideBrandChrome
self.pushGatewayBaseURL = pushGatewayBaseURL self.pushGatewayBaseURL = pushGatewayBaseURL
self.oidcRedirectURL = oidcRedirectURL self.oAuthRedirectURL = oAuthRedirectURL
self.websiteURL = websiteURL self.websiteURL = websiteURL
self.logoURL = logoURL self.logoURL = logoURL
self.copyrightURL = copyrightURL self.copyrightURL = copyrightURL
@@ -249,19 +249,19 @@ final class AppSettings {
// MARK: - Authentication // MARK: - Authentication
/// Any pre-defined static client registrations for OIDC issuers. /// Any pre-defined static client registrations for OAuth issuers.
let oidcStaticRegistrations: [URL: String] = ["https://id.thirdroom.io/realms/thirdroom": "elementx"] let oAuthStaticRegistrations: [URL: String] = ["https://id.thirdroom.io/realms/thirdroom": "elementx"]
/// The redirect URL used for OIDC. For the normal case we don't actually need the bundle ID as the web authentication session handles the redirect internally. /// The redirect URL used for OAuth. For the normal case we don't actually need the bundle ID as the web authentication session handles the redirect internally.
/// However in the case where MAS sends the user to an external app, we need to make sure that the system will open the correct variant of the app (e.g. Nightly). /// However in the case where MAS sends the user to an external app, we need to make sure that the system will open the correct variant of the app (e.g. Nightly).
private(set) var oidcRedirectURL: URL! = URL(string: "https://element.io/oauth/ios/\(InfoPlistReader.main.bundleIdentifier)") private(set) var oAuthRedirectURL: URL! = URL(string: "https://element.io/oauth/ios/\(InfoPlistReader.main.bundleIdentifier)")
private(set) lazy var oidcConfiguration = OIDCConfiguration(clientName: InfoPlistReader.main.bundleDisplayName, private(set) lazy var oAuthConfiguration = OAuthConfiguration(clientName: InfoPlistReader.main.bundleDisplayName,
redirectURI: oidcRedirectURL, redirectURI: oAuthRedirectURL,
clientURI: websiteURL, clientURI: websiteURL,
logoURI: logoURL, logoURI: logoURL,
tosURI: acceptableUseURL, tosURI: acceptableUseURL,
policyURI: privacyURL, policyURI: privacyURL,
staticRegistrations: oidcStaticRegistrations.mapKeys { $0.absoluteString }) staticRegistrations: oAuthStaticRegistrations.mapKeys { $0.absoluteString })
/// Whether or not the Create Account button is shown on the start screen. /// Whether or not the Create Account button is shown on the start screen.
/// ///

View File

@@ -0,0 +1,36 @@
//
// Copyright 2025 Element Creations Ltd.
// Copyright 2024-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Foundation
import MatrixRustSDK
struct OAuthConfiguration {
let clientName: String
let redirectURI: URL
let clientURI: URL
let logoURI: URL
let tosURI: URL
let policyURI: URL
let staticRegistrations: [String: String]
}
#if canImport(MatrixRustSDK)
import MatrixRustSDK
extension OAuthConfiguration {
var rustValue: MatrixRustSDK.OAuthConfiguration {
MatrixRustSDK.OAuthConfiguration(clientName: clientName,
redirectUri: redirectURI.absoluteString,
clientUri: clientURI.absoluteString,
logoUri: logoURI.absoluteString,
tosUri: tosURI.absoluteString,
policyUri: policyURI.absoluteString,
staticRegistrations: staticRegistrations)
}
}
#endif

View File

@@ -1,35 +0,0 @@
//
// Copyright 2025 Element Creations Ltd.
// Copyright 2024-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Foundation
struct OIDCConfiguration {
let clientName: String
let redirectURI: URL
let clientURI: URL
let logoURI: URL
let tosURI: URL
let policyURI: URL
let staticRegistrations: [String: String]
}
#if canImport(MatrixRustSDK)
import MatrixRustSDK
extension OIDCConfiguration {
var rustValue: OAuthConfiguration {
OAuthConfiguration(clientName: clientName,
redirectUri: redirectURI.absoluteString,
clientUri: clientURI.absoluteString,
logoUri: logoURI.absoluteString,
tosUri: tosURI.absoluteString,
policyUri: policyURI.absoluteString,
staticRegistrations: staticRegistrations)
}
}
#endif

View File

@@ -96,7 +96,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
private let stateMachine: StateMachine<State, Event> private let stateMachine: StateMachine<State, Event>
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private var oidcPresenter: OIDCAuthenticationPresenter? private var oAuthPresenter: OAuthAuthenticationPresenter?
// periphery:ignore - retaining purpose // periphery:ignore - retaining purpose
private var bugReportFlowCoordinator: BugReportFlowCoordinator? private var bugReportFlowCoordinator: BugReportFlowCoordinator?
@@ -151,7 +151,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
} }
func clearRoute(animated: Bool) { func clearRoute(animated: Bool) {
oidcPresenter?.cancel() // Handle ongoing OIDC authentication first. oAuthPresenter?.cancel() // Handle ongoing OAuth authentication first.
switch stateMachine.state { switch stateMachine.state {
case .initial, .startScreen: case .initial, .startScreen:
@@ -175,13 +175,13 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
} }
} }
func handleOIDCRedirectURL(_ url: URL) { func handleOAuthCallbackURL(_ url: URL) {
guard let oidcPresenter else { guard let oAuthPresenter else {
MXLog.error("Failed to find an OIDC request in progress.") MXLog.error("Failed to find an OAuth request in progress.")
return return
} }
oidcPresenter.handleUniversalLinkCallback(url) oAuthPresenter.handleUniversalLinkCallback(url)
} }
// MARK: - Setup // MARK: - Setup
@@ -247,8 +247,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
// Completion // Completion
stateMachine.addRoutes(event: .signedIn, transitions: [.qrCodeLoginScreen => .complete, stateMachine.addRoutes(event: .signedIn, transitions: [.qrCodeLoginScreen => .complete,
.serverConfirmationScreen => .complete, // OIDC authentication .serverConfirmationScreen => .complete, // OAuth authentication
.startScreen => .complete, // Direct OIDC authentication .startScreen => .complete, // Direct OAuth authentication
.loginScreen => .complete]) { [weak self] context in .loginScreen => .complete]) { [weak self] context in
guard let userSession = context.userInfo as? UserSessionProtocol else { fatalError("The user session wasn't included in the context") } guard let userSession = context.userInfo as? UserSessionProtocol else { fatalError("The user session wasn't included in the context") }
self?.userHasSignedIn(userSession: userSession) self?.userHasSignedIn(userSession: userSession)
@@ -300,8 +300,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
case .register: case .register:
stateMachine.tryEvent(.confirmServer(.register)) stateMachine.tryEvent(.confirmServer(.register))
case .loginDirectlyWithOIDC(let oidcData, let window): case .loginDirectlyWithOAuth(let oAuthData, let window):
showOIDCAuthentication(oidcData: oidcData, presentationAnchor: window) showOAuthAuthentication(oAuthData: oAuthData, presentationAnchor: window)
case .loginDirectlyWithPassword(let loginHint): case .loginDirectlyWithPassword(let loginHint):
stateMachine.tryEvent(.continueWithPassword, userInfo: loginHint) stateMachine.tryEvent(.continueWithPassword, userInfo: loginHint)
@@ -335,8 +335,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
switch action { switch action {
case .startOver: case .startOver:
fatalError("QR code login shouldn't request to start over as it's handled within the screen.") fatalError("QR code login shouldn't request to start over as it's handled within the screen.")
case .requestOIDCAuthorisation, .linkedDevice: case .requestOAuthAuthorisation, .linkedDevice:
fatalError("QR code login shouldn't request an OIDC flow or link a device.") fatalError("QR code login shouldn't request an OAuth flow or link a device.")
case .signInManually: case .signInManually:
navigationStackCoordinator.setSheetCoordinator(nil) navigationStackCoordinator.setSheetCoordinator(nil)
stateMachine.tryEvent(.cancelledLoginWithQR) stateMachine.tryEvent(.cancelledLoginWithQR)
@@ -374,8 +374,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
guard let self else { return } guard let self else { return }
switch action { switch action {
case .continueWithOIDC(let oidcData, let window): case .continueWithOAuth(let oAuthData, let window):
showOIDCAuthentication(oidcData: oidcData, presentationAnchor: window) showOAuthAuthentication(oAuthData: oAuthData, presentationAnchor: window)
case .continueWithPassword: case .continueWithPassword:
stateMachine.tryEvent(.continueWithPassword) stateMachine.tryEvent(.continueWithPassword)
case .changeServer: case .changeServer:
@@ -420,22 +420,22 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
/// **Note:** We have intentionally excluded this presentation from the state machine as it doesn't mutate our navigation stack and there /// **Note:** We have intentionally excluded this presentation from the state machine as it doesn't mutate our navigation stack and there
/// isn't a robust way to detect why the user returned to the app when the MAS URL directly opens an external app for authentication without /// isn't a robust way to detect why the user returned to the app when the MAS URL directly opens an external app for authentication without
/// presenting a web authentication session. /// presenting a web authentication session.
private func showOIDCAuthentication(oidcData: OIDCAuthorizationDataProxy, presentationAnchor: UIWindow) { private func showOAuthAuthentication(oAuthData: OAuthAuthorizationDataProxy, presentationAnchor: UIWindow) {
let presenter = OIDCAuthenticationPresenter(authenticationService: authenticationService, let presenter = OAuthAuthenticationPresenter(authenticationService: authenticationService,
oidcRedirectURL: appSettings.oidcRedirectURL, redirectURL: appSettings.oAuthRedirectURL,
presentationAnchor: presentationAnchor, presentationAnchor: presentationAnchor,
appMediator: appMediator, appMediator: appMediator,
userIndicatorController: userIndicatorController) userIndicatorController: userIndicatorController)
oidcPresenter = presenter oAuthPresenter = presenter
Task { Task {
switch await presenter.authenticate(using: oidcData) { switch await presenter.authenticate(using: oAuthData) {
case .success(let userSession): case .success(let userSession):
stateMachine.tryEvent(.signedIn, userInfo: userSession) stateMachine.tryEvent(.signedIn, userInfo: userSession)
case .failure: case .failure:
break // Nothing to do, any alerts will be handled by the presenter. break // Nothing to do, any alerts will be handled by the presenter.
} }
oidcPresenter = nil oAuthPresenter = nil
} }
} }
@@ -454,8 +454,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
switch action { switch action {
case .signedIn(let userSession): case .signedIn(let userSession):
stateMachine.tryEvent(.signedIn, userInfo: userSession) stateMachine.tryEvent(.signedIn, userInfo: userSession)
case .configuredForOIDC: case .configuredForOAuth:
// Pop back to the confirmation screen for OIDC login to continue. // Pop back to the confirmation screen for OAuth login to continue.
navigationStackCoordinator.pop(animated: false) navigationStackCoordinator.pop(animated: false)
} }
} }

View File

@@ -174,7 +174,7 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
} }
case .globalSearch: case .globalSearch:
presentGlobalSearch() presentGlobalSearch()
case .accountProvisioningLink, .oidcCallback, .settings, .chatBackupSettings, .call: case .accountProvisioningLink, .oAuthCallback, .settings, .chatBackupSettings, .call:
break // These routes cannot be handled. break // These routes cannot be handled.
} }
} }

View File

@@ -122,8 +122,8 @@ class EncryptionResetFlowCoordinator: FlowCoordinatorProtocol {
guard let self else { return } guard let self else { return }
switch action { switch action {
case .requestOIDCAuthorisation(let url): case .requestOAuthAuthorisation(let url):
presentOIDCAuthorization(for: url) presentOAuthAuthorization(for: url)
case .requestPassword(let passwordPublisher): case .requestPassword(let passwordPublisher):
stateMachine.tryEvent(.confirmPassword, userInfo: passwordPublisher) stateMachine.tryEvent(.confirmPassword, userInfo: passwordPublisher)
case .cancel: case .cancel:
@@ -155,14 +155,14 @@ class EncryptionResetFlowCoordinator: FlowCoordinatorProtocol {
} }
} }
private var accountSettingsPresenter: OIDCAccountSettingsPresenter? private var accountSettingsPresenter: OAuthAccountSettingsPresenter?
private func presentOIDCAuthorization(for url: URL) { private func presentOAuthAuthorization(for url: URL) {
// Note to anyone in the future if you come back here to make this open in Safari instead of a WAS. // Note to anyone in the future if you come back here to make this open in Safari instead of a WAS.
// As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷 // As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷
accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: url, accountSettingsPresenter = OAuthAccountSettingsPresenter(accountURL: url,
presentationAnchor: windowManager.mainWindow, presentationAnchor: windowManager.mainWindow,
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings) appSettings: appSettings)
accountSettingsPresenter?.start() accountSettingsPresenter?.start()
} }
} }

View File

@@ -80,7 +80,7 @@ class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol {
MXLog.info("Handling app route: \(appRoute)") MXLog.info("Handling app route: \(appRoute)")
switch appRoute { switch appRoute {
case .accountProvisioningLink, .oidcCallback: case .accountProvisioningLink, .oAuthCallback:
break // We always ignore these flows when logged in. break // We always ignore these flows when logged in.
case .roomList, .room, .roomAlias, .childRoom, .childRoomAlias, case .roomList, .room, .roomAlias, .childRoom, .childRoomAlias,
.roomDetails, .roomMemberDetails, .userProfile, .thread, .roomDetails, .roomMemberDetails, .userProfile, .thread,

View File

@@ -9,7 +9,7 @@ import Combine
import Foundation import Foundation
enum LinkNewDeviceFlowCoordinatorAction { enum LinkNewDeviceFlowCoordinatorAction {
case requestOIDCAuthorisation(URL, OIDCAccountSettingsPresenter.Continuation) case requestOAuthAuthorisation(URL, OAuthAccountSettingsPresenter.Continuation)
case dismiss case dismiss
} }
@@ -77,8 +77,8 @@ class LinkNewDeviceFlowCoordinator: FlowCoordinatorProtocol {
fatalError("QR linking shouldn't send sign-in actions.") fatalError("QR linking shouldn't send sign-in actions.")
case .startOver: case .startOver:
navigationStackCoordinator.pop() // Pops back to the LinkNewDeviceScreen. navigationStackCoordinator.pop() // Pops back to the LinkNewDeviceScreen.
case .requestOIDCAuthorisation(let url, let continuation): case .requestOAuthAuthorisation(let url, let continuation):
actionsSubject.send(.requestOIDCAuthorisation(url, continuation)) actionsSubject.send(.requestOAuthAuthorisation(url, continuation))
case .linkedDevice: case .linkedDevice:
actionsSubject.send(.dismiss) actionsSubject.send(.dismiss)
case .cancel: case .cancel:

View File

@@ -201,7 +201,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
} }
case .roomAlias, .childRoomAlias, .eventOnRoomAlias, .childEventOnRoomAlias: case .roomAlias, .childRoomAlias, .eventOnRoomAlias, .childEventOnRoomAlias:
break // These are converted to a room ID route one level above. break // These are converted to a room ID route one level above.
case .accountProvisioningLink, .oidcCallback, .roomList, .userProfile, .call, .settings, .chatBackupSettings, .globalSearch: case .accountProvisioningLink, .oAuthCallback, .roomList, .userProfile, .call, .settings, .chatBackupSettings, .globalSearch:
break // These routes can't be handled. break // These routes can't be handled.
case .transferOwnership(let roomID): case .transferOwnership(let roomID):
guard self.roomID == roomID else { fatalError("Navigation route doesn't belong to this room flow.") } guard self.roomID == roomID else { fatalError("Navigation route doesn't belong to this room flow.") }

View File

@@ -120,7 +120,7 @@ final class RoomMembersFlowCoordinator: FlowCoordinatorProtocol {
} }
case .roomAlias, .childRoomAlias, .eventOnRoomAlias, .childEventOnRoomAlias: case .roomAlias, .childRoomAlias, .eventOnRoomAlias, .childEventOnRoomAlias:
break // These are converted to a room ID route one level above. break // These are converted to a room ID route one level above.
case .accountProvisioningLink, .oidcCallback, case .accountProvisioningLink, .oAuthCallback,
.roomList, .room, .roomDetails, .event, .roomList, .room, .roomDetails, .event,
.userProfile, .call, .settings, .chatBackupSettings, .userProfile, .call, .settings, .chatBackupSettings,
.share, .transferOwnership, .thread, .globalSearch: .share, .transferOwnership, .thread, .globalSearch:

View File

@@ -188,7 +188,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
switch action { switch action {
case .dismiss: case .dismiss:
navigationStackCoordinator.setSheetCoordinator(nil) navigationStackCoordinator.setSheetCoordinator(nil)
case .requestOIDCAuthorisation(let url, let continuation): case .requestOAuthAuthorisation(let url, let continuation):
presentAccountManagementURL(url, continuation: continuation) presentAccountManagementURL(url, continuation: continuation)
} }
} }
@@ -295,17 +295,17 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
} }
// MARK: OIDC Account Management // MARK: OAuth Account Management
private var accountSettingsPresenter: OIDCAccountSettingsPresenter? private var accountSettingsPresenter: OAuthAccountSettingsPresenter?
private func presentAccountManagementURL(_ url: URL, continuation: OIDCAccountSettingsPresenter.Continuation? = nil) { private func presentAccountManagementURL(_ url: URL, continuation: OAuthAccountSettingsPresenter.Continuation? = nil) {
// Note to anyone in the future if you come back here to make this open in Safari instead of a WAS. // Note to anyone in the future if you come back here to make this open in Safari instead of a WAS.
// As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷 // As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷
accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: url, accountSettingsPresenter = OAuthAccountSettingsPresenter(accountURL: url,
presentationAnchor: flowParameters.windowManager.mainWindow, presentationAnchor: flowParameters.windowManager.mainWindow,
appMediator: flowParameters.appMediator, appMediator: flowParameters.appMediator,
appSettings: flowParameters.appSettings, appSettings: flowParameters.appSettings,
continuation: continuation) continuation: continuation)
accountSettingsPresenter?.start() accountSettingsPresenter?.start()
} }
} }

View File

@@ -121,7 +121,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
MXLog.info("Handling app route: \(appRoute)") MXLog.info("Handling app route: \(appRoute)")
switch appRoute { switch appRoute {
case .accountProvisioningLink, .oidcCallback: case .accountProvisioningLink, .oAuthCallback:
break // We always ignore these flows when logged in. break // We always ignore these flows when logged in.
case .settings, .chatBackupSettings: case .settings, .chatBackupSettings:
if ProcessInfo.processInfo.isiOSAppOnMac, flowParameters.windowManager.secondaryWindowsEnabled { if ProcessInfo.processInfo.isiOSAppOnMac, flowParameters.windowManager.secondaryWindowsEnabled {

View File

@@ -18,26 +18,26 @@ extension AuthenticationClientFactoryMock {
"example.com": ClientSDKMock(configuration: .init(serverAddress: "example.com", "example.com": ClientSDKMock(configuration: .init(serverAddress: "example.com",
homeserverURL: "https://matrix.example.com", homeserverURL: "https://matrix.example.com",
slidingSyncVersion: .native, slidingSyncVersion: .native,
oidcLoginURL: nil, oAuthLoginURL: nil,
supportsOIDCCreatePrompt: false, supportsOAuthCreatePrompt: false,
supportsPasswordLogin: true)), supportsPasswordLogin: true)),
"company.com": ClientSDKMock(configuration: .init(serverAddress: "company.com", "company.com": ClientSDKMock(configuration: .init(serverAddress: "company.com",
homeserverURL: "https://matrix.company.com", homeserverURL: "https://matrix.company.com",
slidingSyncVersion: .native, slidingSyncVersion: .native,
oidcLoginURL: "https://auth.company.com/oidc", oAuthLoginURL: "https://auth.company.com/login",
supportsOIDCCreatePrompt: false, supportsOAuthCreatePrompt: false,
supportsPasswordLogin: false)), supportsPasswordLogin: false)),
"server.net": ClientSDKMock(configuration: .init(serverAddress: "server.net", "server.net": ClientSDKMock(configuration: .init(serverAddress: "server.net",
homeserverURL: "https://matrix.server.net", homeserverURL: "https://matrix.server.net",
slidingSyncVersion: .native, slidingSyncVersion: .native,
oidcLoginURL: nil, oAuthLoginURL: nil,
supportsOIDCCreatePrompt: false, supportsOAuthCreatePrompt: false,
supportsPasswordLogin: false)), supportsPasswordLogin: false)),
"secure.gov": ClientSDKMock(configuration: .init(serverAddress: "secure.gov", "secure.gov": ClientSDKMock(configuration: .init(serverAddress: "secure.gov",
homeserverURL: "https://ess.secure.gov", homeserverURL: "https://ess.secure.gov",
slidingSyncVersion: .native, slidingSyncVersion: .native,
oidcLoginURL: "https://auth.secure.gov/oidc", oAuthLoginURL: "https://auth.secure.gov/login",
supportsOIDCCreatePrompt: false, supportsOAuthCreatePrompt: false,
supportsPasswordLogin: false, supportsPasswordLogin: false,
elementWellKnown: "{\"version\":1,\"enforce_element_pro\":true}")) elementWellKnown: "{\"version\":1,\"enforce_element_pro\":true}"))
] ]

View File

@@ -17,8 +17,8 @@ extension ClientSDKMock {
var serverAddress = "matrix.org" var serverAddress = "matrix.org"
var homeserverURL = "https://matrix-client.matrix.org" var homeserverURL = "https://matrix-client.matrix.org"
var slidingSyncVersion = SlidingSyncVersion.native var slidingSyncVersion = SlidingSyncVersion.native
var oidcLoginURL: String? = "https://account.matrix.org/authorize" var oAuthLoginURL: String? = "https://account.matrix.org/authorize"
var supportsOIDCCreatePrompt = true var supportsOAuthCreatePrompt = true
var supportsPasswordLogin = true var supportsPasswordLogin = true
var elementWellKnown: String? var elementWellKnown: String?
var validCredentials = (username: "alice", password: "12345678") var validCredentials = (username: "alice", password: "12345678")
@@ -77,8 +77,8 @@ extension HomeserverLoginDetailsSDKMock {
slidingSyncVersionReturnValue = configuration.slidingSyncVersion slidingSyncVersionReturnValue = configuration.slidingSyncVersion
supportsPasswordLoginReturnValue = configuration.supportsPasswordLogin supportsPasswordLoginReturnValue = configuration.supportsPasswordLogin
supportsOauthLoginReturnValue = configuration.oidcLoginURL != nil supportsOauthLoginReturnValue = configuration.oAuthLoginURL != nil
supportedOauthPromptsReturnValue = switch (configuration.oidcLoginURL, configuration.supportsOIDCCreatePrompt) { supportedOauthPromptsReturnValue = switch (configuration.oAuthLoginURL, configuration.supportsOAuthCreatePrompt) {
case (.none, _): [] case (.none, _): []
case (.some, true): [.consent, .create] case (.some, true): [.consent, .create]
case (.some, false): [.consent] case (.some, false): [.consent]
@@ -91,6 +91,6 @@ extension OAuthAuthorizationDataSDKMock {
convenience init(configuration: ClientSDKMock.Configuration) { convenience init(configuration: ClientSDKMock.Configuration) {
self.init() self.init()
loginUrlReturnValue = configuration.oidcLoginURL loginUrlReturnValue = configuration.oAuthLoginURL
} }
} }

View File

@@ -46,17 +46,17 @@ struct LoginHomeserver: Equatable {
extension LoginHomeserver { extension LoginHomeserver {
/// A mock homeserver that is configured just like matrix.org. /// A mock homeserver that is configured just like matrix.org.
static var mockMatrixDotOrg: LoginHomeserver { static var mockMatrixDotOrg: LoginHomeserver {
LoginHomeserver(address: "matrix.org", loginMode: .oidc(supportsCreatePrompt: true)) LoginHomeserver(address: "matrix.org", loginMode: .oAuth(supportsCreatePrompt: true))
} }
/// A mock homeserver that supports login and registration via a password but has no SSO providers. /// A mock homeserver that supports login and registration via a password but has no OAuth support.
static var mockBasicServer: LoginHomeserver { static var mockBasicServer: LoginHomeserver {
LoginHomeserver(address: "example.com", loginMode: .password) LoginHomeserver(address: "example.com", loginMode: .password)
} }
/// A mock homeserver that supports only supports authentication via a single SSO provider. /// A mock homeserver that supports only supports authentication via OAuth.
static var mockOIDC: LoginHomeserver { static var mockOAuth: LoginHomeserver {
LoginHomeserver(address: "company.com", loginMode: .oidc(supportsCreatePrompt: false)) LoginHomeserver(address: "company.com", loginMode: .oAuth(supportsCreatePrompt: false))
} }
/// A mock homeserver that only with no supported login flows. /// A mock homeserver that only with no supported login flows.

View File

@@ -13,15 +13,15 @@ enum LoginMode: Equatable {
/// The login mode hasn't been determined yet. /// The login mode hasn't been determined yet.
case unknown case unknown
/// The homeserver supports login via OpenID Connect. /// The homeserver supports login via OpenID Connect.
case oidc(supportsCreatePrompt: Bool) case oAuth(supportsCreatePrompt: Bool)
/// The homeserver supports login with a password. /// The homeserver supports login with a password.
case password case password
/// The homeserver only allows login with unsupported mechanisms. Use fallback instead. /// The homeserver only allows login with unsupported mechanisms. Use fallback instead.
case unsupported case unsupported
var supportsOIDCFlow: Bool { var supportsOAuthFlow: Bool {
switch self { switch self {
case .oidc: true case .oAuth: true
default: false default: false
} }
} }

View File

@@ -20,8 +20,8 @@ struct LoginScreenCoordinatorParameters {
} }
enum LoginScreenCoordinatorAction { enum LoginScreenCoordinatorAction {
/// The homeserver was updated to one that supports OIDC. /// The homeserver was updated to one that supports OAuth.
case configuredForOIDC case configuredForOAuth
/// Login was successful. /// Login was successful.
case signedIn(UserSessionProtocol) case signedIn(UserSessionProtocol)
} }
@@ -62,8 +62,8 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
guard let self else { return } guard let self else { return }
switch action { switch action {
case .configuredForOIDC: case .configuredForOAuth:
actionsSubject.send(.configuredForOIDC) actionsSubject.send(.configuredForOAuth)
case .signedIn(let userSession): case .signedIn(let userSession):
actionsSubject.send(.signedIn(userSession)) actionsSubject.send(.signedIn(userSession))
} }

View File

@@ -9,14 +9,14 @@
import Foundation import Foundation
enum LoginScreenViewModelAction { enum LoginScreenViewModelAction {
/// The homeserver was updated to one that supports OIDC. /// The homeserver was updated to one that supports OAuth.
case configuredForOIDC case configuredForOAuth
/// Login was successful. /// Login was successful.
case signedIn(UserSessionProtocol) case signedIn(UserSessionProtocol)
var isConfiguredForOIDC: Bool { var isConfiguredForOAuth: Bool {
switch self { switch self {
case .configuredForOIDC: true case .configuredForOAuth: true
default: false default: false
} }
} }

View File

@@ -78,8 +78,8 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
Task { Task {
switch await authenticationService.configure(for: homeserverDomain, flow: .login) { switch await authenticationService.configure(for: homeserverDomain, flow: .login) {
case .success: case .success:
if authenticationService.homeserver.value.loginMode.supportsOIDCFlow { if authenticationService.homeserver.value.loginMode.supportsOAuthFlow {
actionsSubject.send(.configuredForOIDC) actionsSubject.send(.configuredForOAuth)
} }
stopLoading() stopLoading()
case .failure(let error): case .failure(let error):

View File

@@ -27,7 +27,7 @@ struct LoginScreen: View {
switch context.viewState.loginMode { switch context.viewState.loginMode {
case .password: case .password:
loginForm loginForm
case .oidc: case .oAuth:
// This should never be shown. // This should never be shown.
ProgressView() ProgressView()
default: default:
@@ -102,7 +102,7 @@ struct LoginScreen: View {
} }
} }
/// Text shown if neither password or OIDC login is supported. /// Text shown if neither password or OAuth login is supported.
var loginUnavailableText: some View { var loginUnavailableText: some View {
Text(L10n.screenLoginErrorUnsupportedAuthentication) Text(L10n.screenLoginErrorUnsupportedAuthentication)
.font(.body) .font(.body)

View File

@@ -8,15 +8,15 @@
import AuthenticationServices import AuthenticationServices
/// Presents a web authentication session for an OIDC request. /// Presents a web authentication session for an OAuth request.
/// ///
/// In certain instances the URL may require opening an external app instead of using a WAS. Because of this /// In certain instances the URL may require opening an external app instead of using a WAS. Because of this
/// it is recommended to not encode the OIDC authentication within any state machines, as there is no guarantee /// it is recommended to not encode the OAuth authentication within any state machines, as there is no guarantee
/// that any cancellations/failures will be communicated upwards. /// that any cancellations/failures will be communicated upwards.
@MainActor @MainActor
class OIDCAuthenticationPresenter: NSObject { class OAuthAuthenticationPresenter: NSObject {
private let authenticationService: AuthenticationServiceProtocol private let authenticationService: AuthenticationServiceProtocol
private let oidcRedirectURL: URL private let redirectURL: URL
private let presentationAnchor: UIWindow private let presentationAnchor: UIWindow
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -36,12 +36,12 @@ class OIDCAuthenticationPresenter: NSObject {
private var activeRequest: Request? private var activeRequest: Request?
init(authenticationService: AuthenticationServiceProtocol, init(authenticationService: AuthenticationServiceProtocol,
oidcRedirectURL: URL, redirectURL: URL,
presentationAnchor: UIWindow, presentationAnchor: UIWindow,
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.authenticationService = authenticationService self.authenticationService = authenticationService
self.oidcRedirectURL = oidcRedirectURL self.redirectURL = redirectURL
self.presentationAnchor = presentationAnchor self.presentationAnchor = presentationAnchor
self.appMediator = appMediator self.appMediator = appMediator
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -53,11 +53,11 @@ class OIDCAuthenticationPresenter: NSObject {
/// **Note:** The failure case cannot be relied upon as a signal that the authentication has ended. /// **Note:** The failure case cannot be relied upon as a signal that the authentication has ended.
/// In particular if the authentication URL requires opening an external app, then the user may return /// In particular if the authentication URL requires opening an external app, then the user may return
/// to the app without completing (or cancelling) the authentication. /// to the app without completing (or cancelling) the authentication.
func authenticate(using oidcData: OIDCAuthorizationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> { func authenticate(using oAuthData: OAuthAuthorizationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
let response = await withCheckedContinuation { continuation in let response = await withCheckedContinuation { continuation in
let authenticationURL = oidcData.url let authenticationURL = oAuthData.url
let session = ASWebAuthenticationSession(url: authenticationURL, callback: .oidcRedirectURL(oidcRedirectURL)) { url, error in let session = ASWebAuthenticationSession(url: authenticationURL, callback: .oAuthRedirectURL(redirectURL)) { url, error in
MXLog.info("Handling callback from the session.") MXLog.info("Handling callback from the session.")
continuation.resume(returning: Response(url: url, isExternal: false, error: error)) continuation.resume(returning: Response(url: url, isExternal: false, error: error))
} }
@@ -86,30 +86,30 @@ class OIDCAuthenticationPresenter: NSObject {
guard let url = response.url else { guard let url = response.url else {
// Check for user cancellation (on the WAS sheet) to avoid showing an alert in that instance. // Check for user cancellation (on the WAS sheet) to avoid showing an alert in that instance.
if response.error?.isOIDCUserCancellation == true { if response.error?.isOAuthUserCancellation == true {
// No need to show an error here, just abort and return a failure. // No need to show an error here, just abort and return a failure.
await authenticationService.abortOIDCLogin(data: oidcData) await authenticationService.abortOAuthLogin(data: oAuthData)
return .failure(.oidcError(.userCancellation)) return .failure(.oAuthError(.userCancellation))
} }
let errorDescription = response.error.map(String.init(describing:)) ?? "Unknown error" let errorDescription = response.error.map(String.init(describing:)) ?? "Unknown error"
MXLog.error("Missing callback URL from the web authentication session: \(errorDescription)") MXLog.error("Missing callback URL from the web authentication session: \(errorDescription)")
showFailureIndicator() showFailureIndicator()
await authenticationService.abortOIDCLogin(data: oidcData) await authenticationService.abortOAuthLogin(data: oAuthData)
return .failure(.oidcError(.unknown)) return .failure(.oAuthError(.unknown))
} }
// Exchanging the callback with the homeserver can be slow, so show the loading indicator while we wait (the modal has already been dismissed). // Exchanging the callback with the homeserver can be slow, so show the loading indicator while we wait (the modal has already been dismissed).
startLoading(delay: .milliseconds(50)) // Small delay to handle a cancellation callback without the indicator showing. startLoading(delay: .milliseconds(50)) // Small delay to handle a cancellation callback without the indicator showing.
defer { stopLoading() } defer { stopLoading() }
switch await authenticationService.loginWithOIDCCallback(url) { switch await authenticationService.loginWithOAuthCallback(url) {
case .success(let userSession): case .success(let userSession):
return .success(userSession) return .success(userSession)
case .failure(.oidcError(.userCancellation)): // Check for user cancellation (on the MAS web page) case .failure(.oAuthError(.userCancellation)): // Check for user cancellation (on the MAS web page)
// No need to show an error here, just return the failure. // No need to show an error here, just return the failure.
return .failure(.oidcError(.userCancellation)) return .failure(.oAuthError(.userCancellation))
case .failure(let error): case .failure(let error):
MXLog.error("Error occurred: \(error)") MXLog.error("Error occurred: \(error)")
showFailureIndicator() showFailureIndicator()
@@ -163,20 +163,20 @@ class OIDCAuthenticationPresenter: NSObject {
// MARK: ASWebAuthenticationPresentationContextProviding // MARK: ASWebAuthenticationPresentationContextProviding
extension OIDCAuthenticationPresenter: ASWebAuthenticationPresentationContextProviding { extension OAuthAuthenticationPresenter: ASWebAuthenticationPresentationContextProviding {
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
presentationAnchor presentationAnchor
} }
} }
extension ASWebAuthenticationSession.Callback { extension ASWebAuthenticationSession.Callback {
static func oidcRedirectURL(_ url: URL) -> Self { static func oAuthRedirectURL(_ url: URL) -> Self {
if url.scheme == "https", let host = url.host() { if url.scheme == "https", let host = url.host() {
.https(host: host, path: url.path()) .https(host: host, path: url.path())
} else if let scheme = url.scheme { } else if let scheme = url.scheme {
.customScheme(scheme) .customScheme(scheme)
} else { } else {
fatalError("Invalid OIDC redirect URL: \(url)") fatalError("Invalid OAuth redirect URL: \(url)")
} }
} }
} }
@@ -184,7 +184,7 @@ extension ASWebAuthenticationSession.Callback {
// MARK: - Helpers // MARK: - Helpers
extension Error { extension Error {
var isOIDCUserCancellation: Bool { var isOAuthUserCancellation: Bool {
let nsError = self as NSError let nsError = self as NSError
if nsError.domain == ASWebAuthenticationSessionErrorDomain, if nsError.domain == ASWebAuthenticationSessionErrorDomain,

View File

@@ -17,7 +17,7 @@ struct ServerConfirmationScreenCoordinatorParameters {
} }
enum ServerConfirmationScreenCoordinatorAction { enum ServerConfirmationScreenCoordinatorAction {
case continueWithOIDC(data: OIDCAuthorizationDataProxy, window: UIWindow) case continueWithOAuth(data: OAuthAuthorizationDataProxy, window: UIWindow)
case continueWithPassword case continueWithPassword
case changeServer case changeServer
} }
@@ -50,8 +50,8 @@ final class ServerConfirmationScreenCoordinator: CoordinatorProtocol {
guard let self else { return } guard let self else { return }
switch action { switch action {
case .continueWithOIDC(let oidcData, let window): case .continueWithOAuth(let oAuthData, let window):
actionsSubject.send(.continueWithOIDC(data: oidcData, window: window)) actionsSubject.send(.continueWithOAuth(data: oAuthData, window: window))
case .continueWithPassword: case .continueWithPassword:
actionsSubject.send(.continueWithPassword) actionsSubject.send(.continueWithPassword)
case .changeServer: case .changeServer:

View File

@@ -9,8 +9,8 @@
import SwiftUI import SwiftUI
enum ServerConfirmationScreenViewModelAction { enum ServerConfirmationScreenViewModelAction {
/// Continue the flow using the provided OIDC parameters. /// Continue the flow using the provided OAuth parameters.
case continueWithOIDC(data: OIDCAuthorizationDataProxy, window: UIWindow) case continueWithOAuth(data: OAuthAuthorizationDataProxy, window: UIWindow)
/// Continue the flow using password authentication. /// Continue the flow using password authentication.
case continueWithPassword case continueWithPassword
/// The user would like to change to a different homeserver. /// The user would like to change to a different homeserver.
@@ -29,7 +29,7 @@ struct ServerConfirmationScreenViewState: BindableState {
var mode: ServerConfirmationScreenMode var mode: ServerConfirmationScreenMode
/// The flow being attempted on the selected homeserver. /// The flow being attempted on the selected homeserver.
let authenticationFlow: AuthenticationFlow let authenticationFlow: AuthenticationFlow
/// The presentation anchor used for OIDC authentication. /// The presentation anchor used for OAuth authentication.
var window: UIWindow? var window: UIWindow?
var bindings = ServerConfirmationScreenBindings() var bindings = ServerConfirmationScreenBindings()
@@ -76,7 +76,7 @@ struct ServerConfirmationScreenBindings {
} }
enum ServerConfirmationScreenViewAction { enum ServerConfirmationScreenViewAction {
/// Updates the window used as the OIDC presentation anchor. /// Updates the window used as the OAuth presentation anchor.
case updateWindow(UIWindow) case updateWindow(UIWindow)
/// The user would like to continue with the current homeserver. /// The user would like to continue with the current homeserver.
case confirm case confirm

View File

@@ -133,7 +133,7 @@ class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType,
} }
private func fetchLoginURLIfNeededAndContinue() async { private func fetchLoginURLIfNeededAndContinue() async {
guard authenticationService.homeserver.value.loginMode.supportsOIDCFlow else { guard authenticationService.homeserver.value.loginMode.supportsOAuthFlow else {
actionsSubject.send(.continueWithPassword) actionsSubject.send(.continueWithPassword)
return return
} }
@@ -146,9 +146,9 @@ class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType,
startLoading() // Uses the same ID, so no need to worry if the indicator already exists startLoading() // Uses the same ID, so no need to worry if the indicator already exists
defer { stopLoading() } defer { stopLoading() }
switch await authenticationService.urlForOIDCLogin(loginHint: nil) { switch await authenticationService.urlForOAuthLogin(loginHint: nil) {
case .success(let oidcData): case .success(let oAuthData):
actionsSubject.send(.continueWithOIDC(data: oidcData, window: window)) actionsSubject.send(.continueWithOAuth(data: oAuthData, window: window))
case .failure: case .failure:
displayError(.unknownError) displayError(.unknownError)
} }

View File

@@ -17,7 +17,7 @@ enum MockSoftLogoutScreenState: String, CaseIterable {
// mock that screen. // mock that screen.
case emptyPassword case emptyPassword
case enteredPassword case enteredPassword
case oidc case oAuth
case unsupported case unsupported
case keyBackupNeeded case keyBackupNeeded
@@ -37,9 +37,9 @@ enum MockSoftLogoutScreenState: String, CaseIterable {
homeserver: .mockBasicServer, homeserver: .mockBasicServer,
keyBackupNeeded: false, keyBackupNeeded: false,
password: "12345678") password: "12345678")
case .oidc: case .oAuth:
return SoftLogoutScreenViewModel(credentials: credentials, return SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockOIDC, homeserver: .mockOAuth,
keyBackupNeeded: false) keyBackupNeeded: false)
case .unsupported: case .unsupported:
return SoftLogoutScreenViewModel(credentials: credentials, return SoftLogoutScreenViewModel(credentials: credentials,

View File

@@ -46,7 +46,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
parameters.authenticationService parameters.authenticationService
} }
private var oidcPresenter: OIDCAuthenticationPresenter? private var oAuthPresenter: OAuthAuthenticationPresenter?
var actions: AnyPublisher<SoftLogoutScreenCoordinatorResult, Never> { var actions: AnyPublisher<SoftLogoutScreenCoordinatorResult, Never> {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
@@ -76,8 +76,8 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
showForgotPasswordScreen() showForgotPasswordScreen()
case .clearAllData: case .clearAllData:
actionsSubject.send(.clearAllData) actionsSubject.send(.clearAllData)
case .continueWithOIDC: case .continueWithOAuth:
continueWithOIDC(presentationAnchor: viewModel.context.viewState.window) continueWithOAuth(presentationAnchor: viewModel.context.viewState.window)
} }
} }
.store(in: &cancellables) .store(in: &cancellables)
@@ -91,13 +91,13 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
AnyView(SoftLogoutScreen(context: viewModel.context)) AnyView(SoftLogoutScreen(context: viewModel.context))
} }
func handleOIDCRedirectURL(_ url: URL) { func handleOAuthCallbackURL(_ url: URL) {
guard let oidcPresenter else { guard let oAuthPresenter else {
MXLog.error("Failed to find an OIDC request in progress.") MXLog.error("Failed to find an OAuth request in progress.")
return return
} }
oidcPresenter.handleUniversalLinkCallback(url) oAuthPresenter.handleUniversalLinkCallback(url)
} }
// MARK: - Private // MARK: - Private
@@ -143,32 +143,32 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
} }
} }
private func continueWithOIDC(presentationAnchor: UIWindow?) { private func continueWithOAuth(presentationAnchor: UIWindow?) {
guard let presentationAnchor else { return } guard let presentationAnchor else { return }
startLoading() startLoading()
Task { Task {
switch await authenticationService.urlForOIDCLogin(loginHint: nil) { switch await authenticationService.urlForOAuthLogin(loginHint: nil) {
case .failure(let error): case .failure(let error):
stopLoading() stopLoading()
handleError(error) handleError(error)
case .success(let oidcData): case .success(let oAuthData):
stopLoading() stopLoading()
let presenter = OIDCAuthenticationPresenter(authenticationService: parameters.authenticationService, let presenter = OAuthAuthenticationPresenter(authenticationService: parameters.authenticationService,
oidcRedirectURL: parameters.appSettings.oidcRedirectURL, redirectURL: parameters.appSettings.oAuthRedirectURL,
presentationAnchor: presentationAnchor, presentationAnchor: presentationAnchor,
appMediator: parameters.appMediator, appMediator: parameters.appMediator,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: parameters.userIndicatorController)
self.oidcPresenter = presenter self.oAuthPresenter = presenter
switch await presenter.authenticate(using: oidcData) { switch await presenter.authenticate(using: oAuthData) {
case .success(let userSession): case .success(let userSession):
actionsSubject.send(.signedIn(userSession)) actionsSubject.send(.signedIn(userSession))
case .failure(let error): case .failure(let error):
handleError(error) handleError(error)
} }
self.oidcPresenter = nil self.oAuthPresenter = nil
} }
} }
} }
@@ -180,10 +180,10 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
viewModel.displayError(.alert(L10n.screenLoginErrorInvalidCredentials)) viewModel.displayError(.alert(L10n.screenLoginErrorInvalidCredentials))
case .accountDeactivated: case .accountDeactivated:
viewModel.displayError(.alert(L10n.screenLoginErrorDeactivatedAccount)) viewModel.displayError(.alert(L10n.screenLoginErrorDeactivatedAccount))
case .oidcError(.notSupported): case .oAuthError(.notSupported):
// Temporary alert hijacking the use of .notSupported, can be removed when OIDC support is in the SDK. // Temporary alert hijacking the use of .notSupported, can be removed when OAuth support is in the SDK.
viewModel.displayError(.alert(L10n.commonServerNotSupported)) viewModel.displayError(.alert(L10n.commonServerNotSupported))
case .oidcError(.userCancellation): case .oAuthError(.userCancellation):
// No need to show an error, the user cancelled authentication. // No need to show an error, the user cancelled authentication.
break break
case .sessionTokenRefreshNotSupported: case .sessionTokenRefreshNotSupported:

View File

@@ -22,8 +22,8 @@ enum SoftLogoutScreenViewModelAction: CustomStringConvertible {
case forgotPassword case forgotPassword
/// Clear all user data /// Clear all user data
case clearAllData case clearAllData
/// Continue using OIDC. /// Continue using OAuth.
case continueWithOIDC case continueWithOAuth
/// A string representation of the result, ignoring any associated values that could leak PII. /// A string representation of the result, ignoring any associated values that could leak PII.
var description: String { var description: String {
@@ -34,8 +34,8 @@ enum SoftLogoutScreenViewModelAction: CustomStringConvertible {
return "forgotPassword" return "forgotPassword"
case .clearAllData: case .clearAllData:
return "clearAllData" return "clearAllData"
case .continueWithOIDC: case .continueWithOAuth:
return "continueWithOIDC" return "continueWithOAuth"
} }
} }
} }
@@ -58,7 +58,7 @@ struct SoftLogoutScreenViewState: BindableState {
homeserver.loginMode homeserver.loginMode
} }
/// The presentation anchor used for OIDC authentication. /// The presentation anchor used for OAuth authentication.
var window: UIWindow? var window: UIWindow?
/// Whether to show recover encryption keys message /// Whether to show recover encryption keys message
@@ -80,7 +80,7 @@ struct SoftLogoutScreenBindings {
} }
enum SoftLogoutScreenViewAction { enum SoftLogoutScreenViewAction {
/// Updates the window used as the OIDC presentation anchor. /// Updates the window used as the OAuth presentation anchor.
case updateWindow(UIWindow?) case updateWindow(UIWindow?)
/// Login. /// Login.
case login case login
@@ -88,8 +88,8 @@ enum SoftLogoutScreenViewAction {
case forgotPassword case forgotPassword
/// Clear all user data. /// Clear all user data.
case clearAllData case clearAllData
/// Continue using OIDC. /// Continue using OAuth.
case continueWithOIDC case continueWithOAuth
} }
enum SoftLogoutScreenErrorType: Hashable { enum SoftLogoutScreenErrorType: Hashable {

View File

@@ -38,8 +38,8 @@ class SoftLogoutScreenViewModel: SoftLogoutScreenViewModelType, SoftLogoutScreen
actionsSubject.send(.forgotPassword) actionsSubject.send(.forgotPassword)
case .clearAllData: case .clearAllData:
actionsSubject.send(.clearAllData) actionsSubject.send(.clearAllData)
case .continueWithOIDC: case .continueWithOAuth:
actionsSubject.send(.continueWithOIDC) actionsSubject.send(.continueWithOAuth)
case .updateWindow(let window): case .updateWindow(let window):
guard state.window != window else { return } guard state.window != window else { return }
Task { state.window = window } Task { state.window = window }

View File

@@ -26,8 +26,8 @@ struct SoftLogoutScreen: View {
switch context.viewState.loginMode { switch context.viewState.loginMode {
case .password: case .password:
loginForm loginForm
case .oidc: case .oAuth:
oidcButton oAuthButton
default: default:
loginUnavailableText loginUnavailableText
} }
@@ -98,15 +98,15 @@ struct SoftLogoutScreen: View {
} }
} }
/// The OIDC button that can be used for login. /// The OAuth button that can be used for login.
var oidcButton: some View { var oAuthButton: some View {
Button { context.send(viewAction: .continueWithOIDC) } label: { Button { context.send(viewAction: .continueWithOAuth) } label: {
Text(L10n.actionContinue) Text(L10n.actionContinue)
} }
.buttonStyle(.compound(.primary)) .buttonStyle(.compound(.primary))
} }
/// Text shown if neither password or OIDC login is supported. /// Text shown if neither password or OAuth login is supported.
var loginUnavailableText: some View { var loginUnavailableText: some View {
Text(L10n.screenLoginErrorUnsupportedAuthentication) Text(L10n.screenLoginErrorUnsupportedAuthentication)
.font(.body) .font(.body)

View File

@@ -24,7 +24,7 @@ enum AuthenticationStartScreenCoordinatorAction {
case login case login
case register case register
case loginDirectlyWithOIDC(data: OIDCAuthorizationDataProxy, window: UIWindow) case loginDirectlyWithOAuth(data: OAuthAuthorizationDataProxy, window: UIWindow)
case loginDirectlyWithPassword(loginHint: String?) case loginDirectlyWithPassword(loginHint: String?)
case reportProblem case reportProblem
@@ -65,8 +65,8 @@ final class AuthenticationStartScreenCoordinator: CoordinatorProtocol {
case .register: case .register:
actionsSubject.send(.register) actionsSubject.send(.register)
case .loginDirectlyWithOIDC(let data, let window): case .loginDirectlyWithOAuth(let data, let window):
actionsSubject.send(.loginDirectlyWithOIDC(data: data, window: window)) actionsSubject.send(.loginDirectlyWithOAuth(data: data, window: window))
case .loginDirectlyWithPassword(let loginHint): case .loginDirectlyWithPassword(let loginHint):
actionsSubject.send(.loginDirectlyWithPassword(loginHint: loginHint)) actionsSubject.send(.loginDirectlyWithPassword(loginHint: loginHint))

View File

@@ -13,7 +13,7 @@ enum AuthenticationStartScreenViewModelAction: Equatable {
case login case login
case register case register
case loginDirectlyWithOIDC(data: OIDCAuthorizationDataProxy, window: UIWindow) case loginDirectlyWithOAuth(data: OAuthAuthorizationDataProxy, window: UIWindow)
case loginDirectlyWithPassword(loginHint: String?) case loginDirectlyWithPassword(loginHint: String?)
case reportProblem case reportProblem
@@ -21,7 +21,7 @@ enum AuthenticationStartScreenViewModelAction: Equatable {
} }
struct AuthenticationStartScreenViewState: BindableState { struct AuthenticationStartScreenViewState: BindableState {
/// The presentation anchor used for OIDC authentication. /// The presentation anchor used for OAuth authentication.
var window: UIWindow? var window: UIWindow?
let serverName: String? let serverName: String?
@@ -56,7 +56,7 @@ enum AuthenticationStartScreenAlertType {
} }
enum AuthenticationStartScreenViewAction { enum AuthenticationStartScreenViewAction {
/// Updates the window used as the OIDC presentation anchor. /// Updates the window used as the OAuth presentation anchor.
case updateWindow(UIWindow) case updateWindow(UIWindow)
case developerOptions case developerOptions
case reportProblem case reportProblem

View File

@@ -144,7 +144,7 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType
} }
} }
guard authenticationService.homeserver.value.loginMode.supportsOIDCFlow else { guard authenticationService.homeserver.value.loginMode.supportsOAuthFlow else {
actionsSubject.send(.loginDirectlyWithPassword(loginHint: loginHint)) actionsSubject.send(.loginDirectlyWithPassword(loginHint: loginHint))
return return
} }
@@ -154,9 +154,9 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType
return return
} }
switch await authenticationService.urlForOIDCLogin(loginHint: loginHint) { switch await authenticationService.urlForOAuthLogin(loginHint: loginHint) {
case .success(let oidcData): case .success(let oAuthData):
actionsSubject.send(.loginDirectlyWithOIDC(data: oidcData, window: window)) actionsSubject.send(.loginDirectlyWithOAuth(data: oAuthData, window: window))
case .failure: case .failure:
displayError() displayError()
} }

View File

@@ -10,7 +10,7 @@ import Combine
import SwiftUI import SwiftUI
enum EncryptionResetScreenCoordinatorAction { enum EncryptionResetScreenCoordinatorAction {
case requestOIDCAuthorisation(URL) case requestOAuthAuthorisation(URL)
case requestPassword(passwordPublisher: PassthroughSubject<String, Never>) case requestPassword(passwordPublisher: PassthroughSubject<String, Never>)
case resetFinished case resetFinished
case cancel case cancel
@@ -44,8 +44,8 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol {
guard let self else { return } guard let self else { return }
switch action { switch action {
case .requestOIDCAuthorisation(let url): case .requestOAuthAuthorisation(let url):
self.actionsSubject.send(.requestOIDCAuthorisation(url)) self.actionsSubject.send(.requestOAuthAuthorisation(url))
case .requestPassword(let passwordPublisher): case .requestPassword(let passwordPublisher):
self.actionsSubject.send(.requestPassword(passwordPublisher: passwordPublisher)) self.actionsSubject.send(.requestPassword(passwordPublisher: passwordPublisher))
case .resetFinished: case .resetFinished:

View File

@@ -11,7 +11,7 @@ import Foundation
enum EncryptionResetScreenViewModelAction { enum EncryptionResetScreenViewModelAction {
case requestPassword(passwordPublisher: PassthroughSubject<String, Never>) case requestPassword(passwordPublisher: PassthroughSubject<String, Never>)
case requestOIDCAuthorisation(url: URL) case requestOAuthAuthorisation(url: URL)
case resetFinished case resetFinished
case cancel case cancel
} }

View File

@@ -91,9 +91,9 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
hideLoadingIndicator() hideLoadingIndicator()
actionsSubject.send(.requestOIDCAuthorisation(url: url)) actionsSubject.send(.requestOAuthAuthorisation(url: url))
await resetWithOIDCAuthorisation() await resetWithOAuthAuthorisation()
} }
case .failure(let error): case .failure(let error):
MXLog.error("Failed resetting encryption with error \(error)") MXLog.error("Failed resetting encryption with error \(error)")
@@ -121,7 +121,7 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
} }
} }
private func resetWithOIDCAuthorisation() async { private func resetWithOAuthAuthorisation() async {
guard let identityResetHandle else { guard let identityResetHandle else {
fatalError("Requested reset flow continuation without a stored handle") fatalError("Requested reset flow continuation without a stored handle")
} }

View File

@@ -24,7 +24,7 @@ enum QRCodeLoginScreenCoordinatorAction: CustomStringConvertible {
case startOver case startOver
case signInManually case signInManually
case signedIn(userSession: UserSessionProtocol) case signedIn(userSession: UserSessionProtocol)
case requestOIDCAuthorisation(URL, OIDCAccountSettingsPresenter.Continuation) case requestOAuthAuthorisation(URL, OAuthAccountSettingsPresenter.Continuation)
case linkedDevice case linkedDevice
/// Cancel the flow (dismiss the modal). /// Cancel the flow (dismiss the modal).
case cancel case cancel
@@ -34,7 +34,7 @@ enum QRCodeLoginScreenCoordinatorAction: CustomStringConvertible {
case .startOver: "startOver" case .startOver: "startOver"
case .signInManually: "signInManually" case .signInManually: "signInManually"
case .signedIn: "signedIn" case .signedIn: "signedIn"
case .requestOIDCAuthorisation: "requestOIDCAuthorisation" case .requestOAuthAuthorisation: "requestOAuthAuthorisation"
case .linkedDevice: "linkedDevice" case .linkedDevice: "linkedDevice"
case .cancel: "cancel" case .cancel: "cancel"
} }
@@ -71,8 +71,8 @@ final class QRCodeLoginScreenCoordinator: CoordinatorProtocol {
actionsSubject.send(.startOver) actionsSubject.send(.startOver)
case .signedIn(let userSession): case .signedIn(let userSession):
actionsSubject.send(.signedIn(userSession: userSession)) actionsSubject.send(.signedIn(userSession: userSession))
case .requestOIDCAuthorisation(let url, let continuation): case .requestOAuthAuthorisation(let url, let continuation):
actionsSubject.send(.requestOIDCAuthorisation(url, continuation)) actionsSubject.send(.requestOAuthAuthorisation(url, continuation))
case .linkedDevice: case .linkedDevice:
actionsSubject.send(.linkedDevice) actionsSubject.send(.linkedDevice)
case .cancel: case .cancel:

View File

@@ -16,7 +16,7 @@ enum QRCodeLoginScreenViewModelAction: CustomStringConvertible {
case startOver case startOver
case signInManually case signInManually
case signedIn(userSession: UserSessionProtocol) case signedIn(userSession: UserSessionProtocol)
case requestOIDCAuthorisation(URL, OIDCAccountSettingsPresenter.Continuation) case requestOAuthAuthorisation(URL, OAuthAccountSettingsPresenter.Continuation)
case linkedDevice case linkedDevice
/// Cancel the flow (dismiss the modal). /// Cancel the flow (dismiss the modal).
case cancel case cancel
@@ -26,7 +26,7 @@ enum QRCodeLoginScreenViewModelAction: CustomStringConvertible {
case .startOver: "startOver" case .startOver: "startOver"
case .signInManually: "signInManually" case .signInManually: "signInManually"
case .signedIn: "signedIn" case .signedIn: "signedIn"
case .requestOIDCAuthorisation: "requestOIDCAuthorisation" case .requestOAuthAuthorisation: "requestOAuthAuthorisation"
case .linkedDevice: "linkedDevice" case .linkedDevice: "linkedDevice"
case .cancel: "cancel" case .cancel: "cancel"
} }

View File

@@ -20,7 +20,7 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
} }
private var currentTask: AnyCancellable? private var currentTask: AnyCancellable?
private var oidcResultTask: AnyCancellable? private var oAuthResultTask: AnyCancellable?
init(mode: QRCodeLoginScreenMode, init(mode: QRCodeLoginScreenMode,
canSignInManually: Bool, canSignInManually: Bool,
@@ -182,7 +182,7 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
case .establishingSecureChannel(let checkCodeString): case .establishingSecureChannel(let checkCodeString):
state.state = .displayCode(.deviceCode(checkCodeString)) state.state = .displayCode(.deviceCode(checkCodeString))
case .waitingForAuthorisation(let url): case .waitingForAuthorisation(let url):
requestOIDCAuthorization(url: url) requestOAuthAuthorization(url: url)
case .syncingSecrets: case .syncingSecrets:
break // Nothing to do. break // Nothing to do.
case .done: case .done:
@@ -222,7 +222,7 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
case .qrScanned(let checkCodeSender): case .qrScanned(let checkCodeSender):
state.state = .confirmCode(.inputCode(checkCodeSender)) state.state = .confirmCode(.inputCode(checkCodeSender))
case .waitingForAuthorisation(let url): case .waitingForAuthorisation(let url):
requestOIDCAuthorization(url: url) requestOAuthAuthorization(url: url)
case .syncingSecrets: case .syncingSecrets:
break // Nothing to do. break // Nothing to do.
case .done: case .done:
@@ -257,11 +257,11 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
} }
} }
private func requestOIDCAuthorization(url: URL) { private func requestOAuthAuthorization(url: URL) {
let (stream, continuation) = AsyncStream<Result<Void, OIDCError>>.makeStream() let (stream, continuation) = AsyncStream<Result<Void, OAuthError>>.makeStream()
actionsSubject.send(.requestOIDCAuthorisation(url, continuation)) actionsSubject.send(.requestOAuthAuthorisation(url, continuation))
oidcResultTask = Task { [weak self] in oAuthResultTask = Task { [weak self] in
for await result in stream { for await result in stream {
guard let self else { return } guard let self else { return }
switch result { switch result {

View File

@@ -15,13 +15,13 @@ import AuthenticationServices
/// have access to this session, and for some reason `prefersEphemeralWebBrowserSession` /// have access to this session, and for some reason `prefersEphemeralWebBrowserSession`
/// isn't sharing the session back to Safari. /// isn't sharing the session back to Safari.
@MainActor @MainActor
class OIDCAccountSettingsPresenter: NSObject { class OAuthAccountSettingsPresenter: NSObject {
private let accountURL: URL private let accountURL: URL
private let oidcRedirectURL: URL private let redirectURL: URL
private let presentationAnchor: UIWindow private let presentationAnchor: UIWindow
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
typealias Continuation = AsyncStream<Result<Void, OIDCError>>.Continuation typealias Continuation = AsyncStream<Result<Void, OAuthError>>.Continuation
private let continuation: Continuation? private let continuation: Continuation?
init(accountURL: URL, init(accountURL: URL,
@@ -30,7 +30,7 @@ class OIDCAccountSettingsPresenter: NSObject {
appSettings: AppSettings, appSettings: AppSettings,
continuation: Continuation? = nil) { continuation: Continuation? = nil) {
self.accountURL = accountURL self.accountURL = accountURL
oidcRedirectURL = appSettings.oidcRedirectURL redirectURL = appSettings.oAuthRedirectURL
self.presentationAnchor = presentationAnchor self.presentationAnchor = presentationAnchor
self.appMediator = appMediator self.appMediator = appMediator
self.continuation = continuation self.continuation = continuation
@@ -40,10 +40,10 @@ class OIDCAccountSettingsPresenter: NSObject {
/// Presents a web authentication session for the supplied data. /// Presents a web authentication session for the supplied data.
func start() { func start() {
let session = ASWebAuthenticationSession(url: accountURL, callback: .oidcRedirectURL(oidcRedirectURL)) { [continuation] _, error in let session = ASWebAuthenticationSession(url: accountURL, callback: .oAuthRedirectURL(redirectURL)) { [continuation] _, error in
guard let continuation else { return } guard let continuation else { return }
if error?.isOIDCUserCancellation == true { if error?.isOAuthUserCancellation == true {
continuation.yield(.failure(.userCancellation)) continuation.yield(.failure(.userCancellation))
} else { } else {
let errorDescription = error.map(String.init(describing:)) ?? "Unknown error" let errorDescription = error.map(String.init(describing:)) ?? "Unknown error"
@@ -70,7 +70,7 @@ class OIDCAccountSettingsPresenter: NSObject {
// MARK: ASWebAuthenticationPresentationContextProviding // MARK: ASWebAuthenticationPresentationContextProviding
extension OIDCAccountSettingsPresenter: ASWebAuthenticationPresentationContextProviding { extension OAuthAccountSettingsPresenter: ASWebAuthenticationPresentationContextProviding {
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
presentationAnchor presentationAnchor
} }

View File

@@ -73,7 +73,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
let loginDetails = await client.homeserverLoginDetails() let loginDetails = await client.homeserverLoginDetails()
homeserver.loginMode = if loginDetails.supportsOauthLogin() { homeserver.loginMode = if loginDetails.supportsOauthLogin() {
.oidc(supportsCreatePrompt: loginDetails.supportedOauthPrompts().contains(.create)) .oAuth(supportsCreatePrompt: loginDetails.supportedOauthPrompts().contains(.create))
} else if loginDetails.supportsPasswordLogin() { } else if loginDetails.supportsPasswordLogin() {
.password .password
} else { } else {
@@ -83,7 +83,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
if flow == .login, homeserver.loginMode == .unsupported { if flow == .login, homeserver.loginMode == .unsupported {
return .failure(.loginNotSupported) return .failure(.loginNotSupported)
} }
if flow == .register, !homeserver.loginMode.supportsOIDCFlow { if flow == .register, !homeserver.loginMode.supportsOAuthFlow {
return .failure(.registrationNotSupported) return .failure(.registrationNotSupported)
} }
@@ -105,39 +105,39 @@ class AuthenticationService: AuthenticationServiceProtocol {
} }
} }
func urlForOIDCLogin(loginHint: String?) async -> Result<OIDCAuthorizationDataProxy, AuthenticationServiceError> { func urlForOAuthLogin(loginHint: String?) async -> Result<OAuthAuthorizationDataProxy, AuthenticationServiceError> {
guard let client else { return .failure(.oidcError(.urlFailure)) } guard let client else { return .failure(.oAuthError(.urlFailure)) }
do { do {
// The create prompt is broken: https://github.com/element-hq/matrix-authentication-service/issues/3429 // The create prompt is broken: https://github.com/element-hq/matrix-authentication-service/issues/3429
// let prompt: OidcPrompt = flow == .register ? .create : .consent // let prompt: OAuthPrompt = flow == .register ? .create : .consent
let oidcData = try await client.urlForOauth(oauthConfiguration: appSettings.oidcConfiguration.rustValue, let oAuthData = try await client.urlForOauth(oauthConfiguration: appSettings.oAuthConfiguration.rustValue,
prompt: .consent, prompt: .consent,
loginHint: loginHint, loginHint: loginHint,
deviceId: nil, deviceId: nil,
additionalScopes: nil) additionalScopes: nil)
return .success(OIDCAuthorizationDataProxy(underlyingData: oidcData)) return .success(OAuthAuthorizationDataProxy(underlyingData: oAuthData))
} catch { } catch {
MXLog.error("Failed to get URL for OIDC login: \(error)") MXLog.error("Failed to get URL for OAuth login: \(error)")
return .failure(.oidcError(.urlFailure)) return .failure(.oAuthError(.urlFailure))
} }
} }
func abortOIDCLogin(data: OIDCAuthorizationDataProxy) async { func abortOAuthLogin(data: OAuthAuthorizationDataProxy) async {
guard let client else { return } guard let client else { return }
MXLog.info("Aborting OIDC login.") MXLog.info("Aborting OAuth login.")
await client.abortOauthAuth(authorizationData: data.underlyingData) await client.abortOauthAuth(authorizationData: data.underlyingData)
} }
func loginWithOIDCCallback(_ callbackURL: URL) async -> Result<UserSessionProtocol, AuthenticationServiceError> { func loginWithOAuthCallback(_ callbackURL: URL) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
guard let client else { return .failure(.failedLoggingIn) } guard let client else { return .failure(.failedLoggingIn) }
do { do {
try await client.loginWithOauthCallback(callbackUrl: callbackURL.absoluteString) try await client.loginWithOauthCallback(callbackUrl: callbackURL.absoluteString)
await verifyClientIfPossible(client: client) await verifyClientIfPossible(client: client)
return await userSession(for: client) return await userSession(for: client)
} catch OAuthError.Cancelled { } catch MatrixRustSDK.OAuthError.Cancelled {
return .failure(.oidcError(.userCancellation)) return .failure(.oAuthError(.userCancellation))
} catch { } catch {
MXLog.error("Login with OIDC failed: \(error)") MXLog.error("Login with OAuth failed: \(error)")
return .failure(.failedLoggingIn) return .failure(.failedLoggingIn)
} }
} }
@@ -149,7 +149,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
let refreshToken = try? client.session().refreshToken let refreshToken = try? client.session().refreshToken
if refreshToken != nil { if refreshToken != nil {
MXLog.warning("Refresh token found for a non oidc session, can't restore session, logging out") MXLog.warning("Refresh token found for a non OAuth session, can't restore session, logging out")
_ = try? await client.logout() _ = try? await client.logout()
return .failure(.sessionTokenRefreshNotSupported) return .failure(.sessionTokenRefreshNotSupported)
} }
@@ -206,7 +206,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
Task { Task {
do { do {
let client = try await makeClient(homeserverAddress: scannedServerNameOrBaseUrl) let client = try await makeClient(homeserverAddress: scannedServerNameOrBaseUrl)
let qrCodeHandler = client.newLoginWithQrCodeHandler(oauthConfiguration: appSettings.oidcConfiguration.rustValue) let qrCodeHandler = client.newLoginWithQrCodeHandler(oauthConfiguration: appSettings.oAuthConfiguration.rustValue)
try await qrCodeHandler.scan(qrCodeData: qrData, progressListener: listener) try await qrCodeHandler.scan(qrCodeData: qrData, progressListener: listener)
// Since the QR code login flow includes verification. // Since the QR code login flow includes verification.
@@ -273,7 +273,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
// MARK: - Classic App // MARK: - Classic App
/// Populates the Classic app account's state by checking whether the account's homeserver is supported /// Populates the Classic app account's state by checking whether the account's homeserver is supported
/// (has Sliding Sync and OIDC or password login) and whether all of the required secrets are available. /// (has Sliding Sync and OAuth or password login) and whether all of the required secrets are available.
func setupClassicAppAccountState() async { func setupClassicAppAccountState() async {
guard let classicAppAccount, classicAppAccount.state.isServerSupported == nil else { return } guard let classicAppAccount, classicAppAccount.state.isServerSupported == nil else { return }
MXLog.info("Checking Classic app account: \(classicAppAccount)") MXLog.info("Checking Classic app account: \(classicAppAccount)")

View File

@@ -19,8 +19,8 @@ enum AuthenticationFlow {
} }
enum AuthenticationServiceError: Error, Equatable { enum AuthenticationServiceError: Error, Equatable {
/// An error occurred during OIDC authentication. /// An error occurred during OAuth authentication.
case oidcError(OIDCError) case oAuthError(OAuthError)
/// An error occurred during login with QR Code. /// An error occurred during login with QR Code.
case qrCodeError(QRCodeLoginError) case qrCodeError(QRCodeLoginError)
@@ -46,12 +46,12 @@ protocol AuthenticationServiceProtocol: QRCodeLoginServiceProtocol {
/// Sets up the service for login on the specified homeserver address. /// Sets up the service for login on the specified homeserver address.
func configure(for homeserverAddress: String, flow: AuthenticationFlow) async -> Result<Void, AuthenticationServiceError> func configure(for homeserverAddress: String, flow: AuthenticationFlow) async -> Result<Void, AuthenticationServiceError>
/// Performs login using OIDC for the current homeserver. /// Performs login using OAuth for the current homeserver.
func urlForOIDCLogin(loginHint: String?) async -> Result<OIDCAuthorizationDataProxy, AuthenticationServiceError> func urlForOAuthLogin(loginHint: String?) async -> Result<OAuthAuthorizationDataProxy, AuthenticationServiceError>
/// Asks the SDK to abort an ongoing OIDC login if we didn't get a callback to complete the request with. /// Asks the SDK to abort an ongoing OAuth login if we didn't get a callback to complete the request with.
func abortOIDCLogin(data: OIDCAuthorizationDataProxy) async func abortOAuthLogin(data: OAuthAuthorizationDataProxy) async
/// Completes an OIDC login that was started using ``urlForOIDCLogin``. /// Completes an OAuth login that was started using ``urlForOAuthLogin``.
func loginWithOIDCCallback(_ callbackURL: URL) async -> Result<UserSessionProtocol, AuthenticationServiceError> func loginWithOAuthCallback(_ callbackURL: URL) async -> Result<UserSessionProtocol, AuthenticationServiceError>
/// Performs a password login using the current homeserver. /// Performs a password login using the current homeserver.
func login(username: String, password: String, initialDeviceName: String?, deviceID: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError> func login(username: String, password: String, initialDeviceName: String?, deviceID: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError>
@@ -70,25 +70,25 @@ protocol AuthenticationServiceProtocol: QRCodeLoginServiceProtocol {
func refreshClassicAppAccountState() async func refreshClassicAppAccountState() async
} }
// MARK: - OIDC // MARK: - OAuth
enum OIDCError: Error { enum OAuthError: Error {
/// Failed to get the URL that should be presented for login. /// Failed to get the URL that should be presented for login.
case urlFailure case urlFailure
/// The user cancelled the login. /// The user cancelled the login.
case userCancellation case userCancellation
/// OIDC isn't supported on the currently configured server. /// OAuth isn't supported on the currently configured server.
case notSupported case notSupported
/// An unknown error occurred. /// An unknown error occurred.
case unknown case unknown
} }
struct OIDCAuthorizationDataProxy: Hashable { struct OAuthAuthorizationDataProxy: Hashable {
let underlyingData: OAuthAuthorizationData let underlyingData: OAuthAuthorizationData
var url: URL { var url: URL {
guard let url = URL(string: underlyingData.loginUrl()) else { guard let url = URL(string: underlyingData.loginUrl()) else {
fatalError("OIDC login URL hasn't been validated.") fatalError("OAuth login URL hasn't been validated.")
} }
return url return url
} }

View File

@@ -405,7 +405,7 @@ class ClientProxy: ClientProxyProtocol {
Task { Task {
await syncService.start() await syncService.start()
// If we are using OIDC we want to cache the account management URL in volatile memory on the SDK side. // If we are using OAuth we want to cache the account management URL in volatile memory on the SDK side.
// To avoid the cache being invalidated while the app is backgrounded, we cache at every sync start. // To avoid the cache being invalidated while the app is backgrounded, we cache at every sync start.
await cacheAccountURL() await cacheAccountURL()
} }

View File

@@ -77,7 +77,7 @@ extension MatrixRustSDK.Session: @retroactive Codable {
userId: container.decode(String.self, forKey: .userId), userId: container.decode(String.self, forKey: .userId),
deviceId: container.decode(String.self, forKey: .deviceId), deviceId: container.decode(String.self, forKey: .deviceId),
homeserverUrl: container.decode(String.self, forKey: .homeserverUrl), homeserverUrl: container.decode(String.self, forKey: .homeserverUrl),
oauthData: container.decodeIfPresent(String.self, forKey: .oidcData), oauthData: container.decodeIfPresent(String.self, forKey: .oauthData),
slidingSyncVersion: .native) slidingSyncVersion: .native)
} }
@@ -88,10 +88,11 @@ extension MatrixRustSDK.Session: @retroactive Codable {
try container.encode(userId, forKey: .userId) try container.encode(userId, forKey: .userId)
try container.encode(deviceId, forKey: .deviceId) try container.encode(deviceId, forKey: .deviceId)
try container.encode(homeserverUrl, forKey: .homeserverUrl) try container.encode(homeserverUrl, forKey: .homeserverUrl)
try container.encode(oauthData, forKey: .oidcData) try container.encode(oauthData, forKey: .oauthData)
} }
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case accessToken, refreshToken, userId, deviceId, homeserverUrl, oidcData, slidingSyncProxy case accessToken, refreshToken, userId, deviceId, homeserverUrl, slidingSyncProxy
case oauthData = "oidcData" // We're using the name from before the MSC was stabilised.
} }
} }

View File

@@ -134,7 +134,7 @@ class MockScreen: Identifiable {
allowOtherAccountProviders: false, allowOtherAccountProviders: false,
hideBrandChrome: false, hideBrandChrome: false,
pushGatewayBaseURL: appSettings.pushGatewayBaseURL, pushGatewayBaseURL: appSettings.pushGatewayBaseURL,
oidcRedirectURL: appSettings.oidcRedirectURL, oAuthRedirectURL: appSettings.oAuthRedirectURL,
websiteURL: appSettings.websiteURL, websiteURL: appSettings.websiteURL,
logoURL: appSettings.logoURL, logoURL: appSettings.logoURL,
copyrightURL: appSettings.copyrightURL, copyrightURL: appSettings.copyrightURL,
@@ -773,7 +773,7 @@ class MockScreen: Identifiable {
switch action { switch action {
case .dismiss: case .dismiss:
navigationRootCoordinator.setSheetCoordinator(nil) navigationRootCoordinator.setSheetCoordinator(nil)
case .requestOIDCAuthorisation: case .requestOAuthAuthorisation:
break break
} }
} }

View File

@@ -23,7 +23,7 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase {
// Server Confirmation: Tap change server button // Server Confirmation: Tap change server button
app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap() app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap()
// Server Selection: Clear the default, enter OIDC server and continue. // Server Selection: Clear the default, enter OAuth server and continue.
app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("example.com\n", app: app) app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("example.com\n", app: app)
// Await for the button to be hittable, since a loader may appear // Await for the button to be hittable, since a loader may appear
@@ -56,7 +56,7 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase {
// Server Confirmation: Tap change server button // Server Confirmation: Tap change server button
app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap() app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap()
// Server Selection: Clear the default, enter OIDC server and continue. // Server Selection: Clear the default, enter OAuth server and continue.
app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("example.com\n", app: app) app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("example.com\n", app: app)
// Await for the button to be hittable, since a loader may appear // Await for the button to be hittable, since a loader may appear
@@ -91,7 +91,7 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase {
// Server Confirmation: Tap change server button // Server Confirmation: Tap change server button
app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap() app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap()
// Server Selection: Clear the default, enter OIDC server and continue. // Server Selection: Clear the default, enter OAuth server and continue.
app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("example.com\n", app: app) app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("example.com\n", app: app)
// Await for the button to be hittable, since a loader may appear // Await for the button to be hittable, since a loader may appear
@@ -111,9 +111,9 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase {
try await app.assertScreenshot() try await app.assertScreenshot()
} }
/// Disabled for now as the looping isn't 100% fool-proof and we have OIDC on the integration tests /// Disabled for now as the looping isn't 100% fool-proof and we have OAuth on the integration tests
/// so this mock version doesn't really add anything to the tests as a whole. /// so this mock version doesn't really add anything to the tests as a whole.
func disabled_testSelectingOIDCServer() { func disabled_testSelectingOAuthServer() {
// Allow this test to run for longer to help with the loop whilst waiting to resolve the // Allow this test to run for longer to help with the loop whilst waiting to resolve the
// webcredentials for the Web Authentication Session (see below). // webcredentials for the Web Authentication Session (see below).
executionTimeAllowance = 300 executionTimeAllowance = 300
@@ -127,7 +127,7 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase {
// Server Confirmation: Tap change server button // Server Confirmation: Tap change server button
app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap() app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap()
// Server Selection: Clear the default, enter OIDC server and continue. // Server Selection: Clear the default, enter OAuth server and continue.
app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("company.com\n", app: app) app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("company.com\n", app: app)
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
@@ -155,7 +155,7 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase {
} }
} }
XCTAssertTrue(wasAlertText.exists, "The web authentication prompt should be shown after selecting a homeserver with OIDC.") XCTAssertTrue(wasAlertText.exists, "The web authentication prompt should be shown after selecting a homeserver with OAuth.")
} }
func testProvisionedLoginWithPassword() async throws { func testProvisionedLoginWithPassword() async throws {

View File

@@ -21,22 +21,22 @@ struct AppRouteURLParserTests {
} }
@Test @Test
func oidcCallbackRoute() { func oAuthCallbackRoute() {
// Given an OIDC callback for this app. // Given an OAuth callback for this app.
let callbackURL = appSettings.oidcRedirectURL.appending(queryItems: [URLQueryItem(name: "state", value: "12345"), let callbackURL = appSettings.oAuthRedirectURL.appending(queryItems: [URLQueryItem(name: "state", value: "12345"),
URLQueryItem(name: "code", value: "67890")]) URLQueryItem(name: "code", value: "67890")])
// When parsing that route. // When parsing that route.
let route = appRouteURLParser.route(from: callbackURL) let route = appRouteURLParser.route(from: callbackURL)
// Then it should be considered a valid OIDC callback. // Then it should be considered a valid OAuth callback.
#expect(route == .oidcCallback(url: callbackURL)) #expect(route == .oAuthCallback(url: callbackURL))
} }
@Test @Test
func oidcCallbackAppVariantRoute() { func oAuthCallbackAppVariantRoute() {
// Given an OIDC callback for a different app variant. // Given an OAuth callback for a different app variant.
let callbackURL = appSettings.oidcRedirectURL let callbackURL = appSettings.oAuthRedirectURL
.deletingLastPathComponent() .deletingLastPathComponent()
.appending(component: "io.element.elementz") .appending(component: "io.element.elementz")
.appending(queryItems: [URLQueryItem(name: "state", value: "12345"), .appending(queryItems: [URLQueryItem(name: "state", value: "12345"),

View File

@@ -45,7 +45,7 @@ struct AuthenticationServiceTests {
} }
@Test @Test
mutating func configureLoginWithOIDC() async throws { mutating func configureLoginWithOAuth() async throws {
try await setup() try await setup()
try await service.configure(for: "matrix.org", flow: .login).get() try await service.configure(for: "matrix.org", flow: .login).get()
@@ -55,7 +55,7 @@ struct AuthenticationServiceTests {
} }
@Test @Test
mutating func configureRegisterWithOIDC() async throws { mutating func configureRegisterWithOAuth() async throws {
try await setup() try await setup()
try await service.configure(for: "matrix.org", flow: .register).get() try await service.configure(for: "matrix.org", flow: .register).get()

View File

@@ -64,15 +64,15 @@ final class AuthenticationStartScreenViewModelTests {
} }
@Test @Test
func provisionedOIDCState() async throws { func provisionedOAuthState() async throws {
// Given a view model that has been provisioned with a server that supports OIDC. // Given a view model that has been provisioned with a server that supports OAuth.
await setupViewModel(provisioningParameters: .init(accountProvider: "company.com", loginHint: "user@company.com")) await setupViewModel(provisioningParameters: .init(accountProvider: "company.com", loginHint: "user@company.com"))
#expect(authenticationService.homeserver.value.loginMode == .unknown) #expect(authenticationService.homeserver.value.loginMode == .unknown)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
// When tapping the login button the authentication service should be used and the screen // When tapping the login button the authentication service should be used and the screen
// should request to continue the flow without any server selection needed. // should request to continue the flow without any server selection needed.
let deferred = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOAuth }
context.send(viewAction: .login) context.send(viewAction: .login)
try await deferred.fulfill() try await deferred.fulfill()
@@ -80,13 +80,13 @@ final class AuthenticationStartScreenViewModelTests {
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.prompt == .consent) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.prompt == .consent)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.loginHint == "user@company.com") #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.loginHint == "user@company.com")
#expect(authenticationService.homeserver.value.loginMode == .oidc(supportsCreatePrompt: false)) #expect(authenticationService.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: false))
} }
@Test @Test
func provisionedPasswordState() async throws { func provisionedPasswordState() async throws {
// Given a view model that has been provisioned with a server that does not support OIDC. // Given a view model that has been provisioned with a server that does not support OAuth.
await setupViewModel(provisioningParameters: .init(accountProvider: "company.com", loginHint: "user@company.com"), supportsOIDC: false) await setupViewModel(provisioningParameters: .init(accountProvider: "company.com", loginHint: "user@company.com"), supportsOAuth: false)
#expect(authenticationService.homeserver.value.loginMode == .unknown) #expect(authenticationService.homeserver.value.loginMode == .unknown)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
@@ -102,8 +102,8 @@ final class AuthenticationStartScreenViewModelTests {
} }
@Test @Test
func singleProviderOIDCState() async throws { func singleProviderOAuthState() async throws {
// Given a view model that for an app that only allows the use of a single provider that supports OIDC. // Given a view model that for an app that only allows the use of a single provider that supports OAuth.
setAllowedAccountProviders(["company.com"]) setAllowedAccountProviders(["company.com"])
await setupViewModel() await setupViewModel()
#expect(authenticationService.homeserver.value.loginMode == .unknown) #expect(authenticationService.homeserver.value.loginMode == .unknown)
@@ -111,7 +111,7 @@ final class AuthenticationStartScreenViewModelTests {
// When tapping the login button the authentication service should be used and the screen // When tapping the login button the authentication service should be used and the screen
// should request to continue the flow without any server selection needed. // should request to continue the flow without any server selection needed.
let deferred = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOAuth }
context.send(viewAction: .login) context.send(viewAction: .login)
try await deferred.fulfill() try await deferred.fulfill()
@@ -119,14 +119,14 @@ final class AuthenticationStartScreenViewModelTests {
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.prompt == .consent) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.prompt == .consent)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.loginHint == nil) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.loginHint == nil)
#expect(authenticationService.homeserver.value.loginMode == .oidc(supportsCreatePrompt: false)) #expect(authenticationService.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: false))
} }
@Test @Test
func singleProviderPasswordState() async throws { func singleProviderPasswordState() async throws {
// Given a view model that for an app that only allows the use of a single provider that does not support OIDC. // Given a view model that for an app that only allows the use of a single provider that does not support OAuth.
setAllowedAccountProviders(["company.com"]) setAllowedAccountProviders(["company.com"])
await setupViewModel(supportsOIDC: false) await setupViewModel(supportsOAuth: false)
#expect(authenticationService.homeserver.value.loginMode == .unknown) #expect(authenticationService.homeserver.value.loginMode == .unknown)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
@@ -156,13 +156,13 @@ final class AuthenticationStartScreenViewModelTests {
// When continuing with the Classic app account the authentication service should be used and the screen // When continuing with the Classic app account the authentication service should be used and the screen
// should request to continue the flow without any server selection needed. // should request to continue the flow without any server selection needed.
let deferred = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOAuth }
context.send(viewAction: .continueWithClassic(classicAppAccount)) context.send(viewAction: .continueWithClassic(classicAppAccount))
try await deferred.fulfill() try await deferred.fulfill()
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedArguments?.homeserverAddress == "company.com") #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedArguments?.homeserverAddress == "company.com")
#expect(authenticationService.homeserver.value.loginMode == .oidc(supportsCreatePrompt: false)) #expect(authenticationService.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: false))
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.loginHint == "mxid:\(classicAppAccount.userID)") #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.loginHint == "mxid:\(classicAppAccount.userID)")
} }
@@ -180,21 +180,21 @@ final class AuthenticationStartScreenViewModelTests {
// When continuing with the Classic app account the authentication service should be used with the direct homeserver URL // When continuing with the Classic app account the authentication service should be used with the direct homeserver URL
// and the screen should request to continue the flow without any server selection needed. // and the screen should request to continue the flow without any server selection needed.
let deferred = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOAuth }
context.send(viewAction: .continueWithClassic(classicAppAccount)) context.send(viewAction: .continueWithClassic(classicAppAccount))
try await deferred.fulfill() try await deferred.fulfill()
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 2) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 2)
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedArguments?.homeserverAddress == "https://matrix.company.com") #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedArguments?.homeserverAddress == "https://matrix.company.com")
#expect(authenticationService.homeserver.value.loginMode == .oidc(supportsCreatePrompt: false)) #expect(authenticationService.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: false))
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.loginHint == "mxid:\(classicAppAccount.userID)") #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.loginHint == "mxid:\(classicAppAccount.userID)")
} }
@Test @Test
func classicAppAccountOnUnsupportedServer() async { func classicAppAccountOnUnsupportedServer() async {
// Given a view model with a Classic app account whose server supports neither OIDC nor password login. // Given a view model with a Classic app account whose server supports neither OAuth nor password login.
let classicAppAccount = makeClassicAppAccount() let classicAppAccount = makeClassicAppAccount()
await setupViewModel(classicAppAccount: classicAppAccount, supportsOIDC: false, supportsPasswordLogin: false) await setupViewModel(classicAppAccount: classicAppAccount, supportsOAuth: false, supportsPasswordLogin: false)
guard case .welcomeBack(let account) = context.viewState.classicAppMode else { guard case .welcomeBack(let account) = context.viewState.classicAppMode else {
Issue.record("Expected classicAppMode to be .welcomeBack") Issue.record("Expected classicAppMode to be .welcomeBack")
return return
@@ -271,7 +271,7 @@ final class AuthenticationStartScreenViewModelTests {
try await deferred.fulfill() try await deferred.fulfill()
// When the user continues with the Classic account again. // When the user continues with the Classic account again.
let deferredAction = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOIDC } let deferredAction = deferFulfillment(viewModel.actions) { $0.isLoginDirectlyWithOAuth }
context.send(viewAction: .continueWithClassic(classicAppAccount)) context.send(viewAction: .continueWithClassic(classicAppAccount))
// Then the flow should continue the login process. // Then the flow should continue the login process.
@@ -282,12 +282,12 @@ final class AuthenticationStartScreenViewModelTests {
private func setupViewModel(classicAppAccount: ClassicAppAccount? = nil, private func setupViewModel(classicAppAccount: ClassicAppAccount? = nil,
provisioningParameters: AccountProvisioningParameters? = nil, provisioningParameters: AccountProvisioningParameters? = nil,
supportsOIDC: Bool = true, supportsOAuth: Bool = true,
supportsPasswordLogin: Bool = true, supportsPasswordLogin: Bool = true,
availableSecrets: ClassicAppAccount.AvailableSecrets = .complete) async { availableSecrets: ClassicAppAccount.AvailableSecrets = .complete) async {
// Manually create a configuration as the default homeserver address setting is immutable. // Manually create a configuration as the default homeserver address setting is immutable.
client = ClientSDKMock(configuration: .init(oidcLoginURL: supportsOIDC ? "https://account.company.com/authorize" : nil, client = ClientSDKMock(configuration: .init(oAuthLoginURL: supportsOAuth ? "https://account.company.com/authorize" : nil,
supportsOIDCCreatePrompt: false, supportsOAuthCreatePrompt: false,
supportsPasswordLogin: supportsPasswordLogin)) supportsPasswordLogin: supportsPasswordLogin))
// Map both the server name and the homeserver URL so fallback lookups work. // Map both the server name and the homeserver URL so fallback lookups work.
let homeserverClients: [String: ClientSDKMock] = ["company.com": client, let homeserverClients: [String: ClientSDKMock] = ["company.com": client,
@@ -321,7 +321,7 @@ final class AuthenticationStartScreenViewModelTests {
notificationCenter: notificationCenter, notificationCenter: notificationCenter,
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
// Add a fake window in order for the OIDC flow to continue // Add a fake window in order for the OAuth flow to continue
viewModel.context.send(viewAction: .updateWindow(UIWindow())) viewModel.context.send(viewAction: .updateWindow(UIWindow()))
} }
@@ -342,7 +342,7 @@ final class AuthenticationStartScreenViewModelTests {
allowOtherAccountProviders: false, allowOtherAccountProviders: false,
hideBrandChrome: false, hideBrandChrome: false,
pushGatewayBaseURL: appSettings.pushGatewayBaseURL, pushGatewayBaseURL: appSettings.pushGatewayBaseURL,
oidcRedirectURL: appSettings.oidcRedirectURL, oAuthRedirectURL: appSettings.oAuthRedirectURL,
websiteURL: appSettings.websiteURL, websiteURL: appSettings.websiteURL,
logoURL: appSettings.logoURL, logoURL: appSettings.logoURL,
copyrightURL: appSettings.copyrightURL, copyrightURL: appSettings.copyrightURL,
@@ -362,9 +362,9 @@ final class AuthenticationStartScreenViewModelTests {
} }
extension AuthenticationStartScreenViewModelAction { extension AuthenticationStartScreenViewModelAction {
var isLoginDirectlyWithOIDC: Bool { var isLoginDirectlyWithOAuth: Bool {
switch self { switch self {
case .loginDirectlyWithOIDC: true case .loginDirectlyWithOAuth: true
default: false default: false
} }
} }

View File

@@ -155,21 +155,21 @@ struct LoginScreenViewModelTests {
} }
@Test @Test
mutating func oidcServer() async throws { mutating func oAuthServer() async throws {
// Given the screen configured for matrix.org // Given the screen configured for matrix.org
await setupViewModel() await setupViewModel()
// When entering a username for a user on a homeserver with OIDC. // When entering a username for a user on a homeserver with OAuth.
let deferred = deferFulfillment(viewModel.actions) { let deferred = deferFulfillment(viewModel.actions) {
$0.isConfiguredForOIDC $0.isConfiguredForOAuth
} }
context.username = "@bob:company.com" context.username = "@bob:company.com"
context.send(viewAction: .parseUsername) context.send(viewAction: .parseUsername)
try await deferred.fulfill() try await deferred.fulfill()
// Then the view state should be updated with the homeserver and show the OIDC button. // Then the view state should be updated with the homeserver and show the OAuth button.
#expect(context.viewState.loginMode.supportsOIDCFlow, #expect(context.viewState.loginMode.supportsOAuthFlow,
"The OIDC button should be shown.") "The OAuth button should be shown.")
} }
@Test @Test

View File

@@ -147,7 +147,7 @@ struct QRCodeLoginScreenViewModelTests {
try await deferred.fulfill() try await deferred.fulfill()
var deferredAction = deferFulfillment(viewModel.actionsPublisher) { action in var deferredAction = deferFulfillment(viewModel.actionsPublisher) { action in
guard case .requestOIDCAuthorisation = action else { return false } guard case .requestOAuthAuthorisation = action else { return false }
return true return true
} }
linkDesktopProgressSubject.send(.waitingForAuthorisation(verificationURL: .homeDirectory)) linkDesktopProgressSubject.send(.waitingForAuthorisation(verificationURL: .homeDirectory))
@@ -183,7 +183,7 @@ struct QRCodeLoginScreenViewModelTests {
try await deferredState.fulfill() try await deferredState.fulfill()
var deferredAction = deferFulfillment(viewModel.actionsPublisher) { action in var deferredAction = deferFulfillment(viewModel.actionsPublisher) { action in
guard case .requestOIDCAuthorisation = action else { return false } guard case .requestOAuthAuthorisation = action else { return false }
return true return true
} }
linkMobileProgressSubject.send(.waitingForAuthorisation(verificationURL: .homeDirectory)) linkMobileProgressSubject.send(.waitingForAuthorisation(verificationURL: .homeDirectory))

View File

@@ -21,7 +21,7 @@ struct ServerConfirmationScreenViewStateTests {
authenticationFlow: .login) authenticationFlow: .login)
#expect(elementDotIoLogin.message == L10n.screenServerConfirmationMessageLoginElementDotIo, "element.io should have a custom message.") #expect(elementDotIoLogin.message == L10n.screenServerConfirmationMessageLoginElementDotIo, "element.io should have a custom message.")
let otherLogin = ServerConfirmationScreenViewState(mode: .confirmation(LoginHomeserver.mockOIDC.address), let otherLogin = ServerConfirmationScreenViewState(mode: .confirmation(LoginHomeserver.mockOAuth.address),
authenticationFlow: .login) authenticationFlow: .login)
#expect(otherLogin.message == "", "Other servers should not show a message.") #expect(otherLogin.message == "", "Other servers should not show a message.")
@@ -36,8 +36,8 @@ struct ServerConfirmationScreenViewStateTests {
authenticationFlow: .register) authenticationFlow: .register)
#expect(matrixDotOrgRegister.message == L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.") #expect(matrixDotOrgRegister.message == L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.")
let oidcRegister = ServerConfirmationScreenViewState(mode: .confirmation(LoginHomeserver.mockOIDC.address), let oAuthRegister = ServerConfirmationScreenViewState(mode: .confirmation(LoginHomeserver.mockOAuth.address),
authenticationFlow: .register) authenticationFlow: .register)
#expect(oidcRegister.message == L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.") #expect(oAuthRegister.message == L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.")
} }
} }

View File

@@ -46,7 +46,7 @@ final class ServerConfirmationScreenViewModelTests {
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
// When continuing from the confirmation screen. // When continuing from the confirmation screen.
let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOAuth }
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
@@ -54,7 +54,7 @@ final class ServerConfirmationScreenViewModelTests {
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.prompt == .consent) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.prompt == .consent)
#expect(service.homeserver.value.loginMode == .oidc(supportsCreatePrompt: true)) #expect(service.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: true))
} }
@Test @Test
@@ -65,13 +65,13 @@ final class ServerConfirmationScreenViewModelTests {
Issue.record("The configuration should succeed.") Issue.record("The configuration should succeed.")
return return
} }
#expect(service.homeserver.value.loginMode == .oidc(supportsCreatePrompt: true)) #expect(service.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: true))
#expect(context.viewState.mode == .confirmation(service.homeserver.value.address)) #expect(context.viewState.mode == .confirmation(service.homeserver.value.address))
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
// When continuing from the confirmation screen. // When continuing from the confirmation screen.
let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOAuth }
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
@@ -91,7 +91,7 @@ final class ServerConfirmationScreenViewModelTests {
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
// When continuing from the confirmation screen. // When continuing from the confirmation screen.
let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOAuth }
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
@@ -100,7 +100,7 @@ final class ServerConfirmationScreenViewModelTests {
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1)
// The create prompt is broken: https://github.com/element-hq/matrix-authentication-service/issues/3429 // The create prompt is broken: https://github.com/element-hq/matrix-authentication-service/issues/3429
// #expect(client.urlForOauthOauthConfigurationPromptReceivedArguments?.prompt == .create) // #expect(client.urlForOauthOauthConfigurationPromptReceivedArguments?.prompt == .create)
#expect(service.homeserver.value.loginMode == .oidc(supportsCreatePrompt: true)) #expect(service.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: true))
} }
@Test @Test
@@ -111,13 +111,13 @@ final class ServerConfirmationScreenViewModelTests {
Issue.record("The configuration should succeed.") Issue.record("The configuration should succeed.")
return return
} }
#expect(service.homeserver.value.loginMode == .oidc(supportsCreatePrompt: true)) #expect(service.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: true))
#expect(context.viewState.mode == .confirmation(service.homeserver.value.address)) #expect(context.viewState.mode == .confirmation(service.homeserver.value.address))
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
// When continuing from the confirmation screen. // When continuing from the confirmation screen.
let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOAuth }
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
@@ -130,8 +130,8 @@ final class ServerConfirmationScreenViewModelTests {
@Test @Test
func confirmPasswordLoginWithoutConfiguration() async throws { func confirmPasswordLoginWithoutConfiguration() async throws {
// Given a view model for login using a service that hasn't been configured (against a server that doesn't support OIDC). // Given a view model for login using a service that hasn't been configured (against a server that doesn't support OAuth).
setupViewModel(authenticationFlow: .login, supportsOIDC: false) setupViewModel(authenticationFlow: .login, supportsOAuth: false)
#expect(service.homeserver.value.loginMode == .unknown) #expect(service.homeserver.value.loginMode == .unknown)
#expect(context.viewState.mode == .confirmation(service.homeserver.value.address)) #expect(context.viewState.mode == .confirmation(service.homeserver.value.address))
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0)
@@ -142,7 +142,7 @@ final class ServerConfirmationScreenViewModelTests {
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
// Then a call to configure service should be made, but not for the OIDC URL. // Then a call to configure service should be made, but not for the OAuth URL.
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
#expect(service.homeserver.value.loginMode == .password) #expect(service.homeserver.value.loginMode == .password)
@@ -151,7 +151,7 @@ final class ServerConfirmationScreenViewModelTests {
@Test @Test
func confirmPasswordLoginAfterConfiguration() async throws { func confirmPasswordLoginAfterConfiguration() async throws {
// Given a view model for login using a service that has already been configured (via the server selection screen). // Given a view model for login using a service that has already been configured (via the server selection screen).
setupViewModel(authenticationFlow: .login, supportsOIDC: false) setupViewModel(authenticationFlow: .login, supportsOAuth: false)
guard case .success = await service.configure(for: viewModel.state.homeserverAddress, flow: .login) else { guard case .success = await service.configure(for: viewModel.state.homeserverAddress, flow: .login) else {
Issue.record("The configuration should succeed.") Issue.record("The configuration should succeed.")
return return
@@ -166,7 +166,7 @@ final class ServerConfirmationScreenViewModelTests {
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
// Then the configured homeserver should be used and no additional client should be built, nor a call to get the OIDC URL. // Then the configured homeserver should be used and no additional client should be built, nor a call to get the OAuth URL.
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
} }
@@ -175,7 +175,7 @@ final class ServerConfirmationScreenViewModelTests {
func registrationNotSupportedAlert() async throws { func registrationNotSupportedAlert() async throws {
// Given a view model for registration using a service that hasn't been configured and the default server doesn't support registration. // Given a view model for registration using a service that hasn't been configured and the default server doesn't support registration.
// Note: We don't currently take the create prompt into account when determining registration support. // Note: We don't currently take the create prompt into account when determining registration support.
setupViewModel(authenticationFlow: .register, supportsOIDC: false, supportsOIDCCreatePrompt: false) setupViewModel(authenticationFlow: .register, supportsOAuth: false, supportsOAuthCreatePrompt: false)
#expect(service.homeserver.value.loginMode == .unknown) #expect(service.homeserver.value.loginMode == .unknown)
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0)
#expect(context.alertInfo == nil) #expect(context.alertInfo == nil)
@@ -193,7 +193,7 @@ final class ServerConfirmationScreenViewModelTests {
@Test @Test
func loginNotSupportedAlert() async throws { func loginNotSupportedAlert() async throws {
// Given a view model for login using a service that hasn't been configured and the default server doesn't support login. // Given a view model for login using a service that hasn't been configured and the default server doesn't support login.
setupViewModel(authenticationFlow: .login, supportsOIDC: false, supportsOIDCCreatePrompt: false, supportsPasswordLogin: false) setupViewModel(authenticationFlow: .login, supportsOAuth: false, supportsOAuthCreatePrompt: false, supportsPasswordLogin: false)
#expect(service.homeserver.value.loginMode == .unknown) #expect(service.homeserver.value.loginMode == .unknown)
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0)
#expect(context.alertInfo == nil) #expect(context.alertInfo == nil)
@@ -211,7 +211,7 @@ final class ServerConfirmationScreenViewModelTests {
@Test @Test
func elementProRequired() async throws { func elementProRequired() async throws {
// Given a view model for login using a service that hasn't been configured and the default server requires Element Pro. // Given a view model for login using a service that hasn't been configured and the default server requires Element Pro.
setupViewModel(authenticationFlow: .login, supportsOIDC: false, supportsOIDCCreatePrompt: false, supportsPasswordLogin: false, requiresElementPro: true) setupViewModel(authenticationFlow: .login, supportsOAuth: false, supportsOAuthCreatePrompt: false, supportsPasswordLogin: false, requiresElementPro: true)
#expect(service.homeserver.value.loginMode == .unknown) #expect(service.homeserver.value.loginMode == .unknown)
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0)
#expect(context.alertInfo == nil) #expect(context.alertInfo == nil)
@@ -238,7 +238,7 @@ final class ServerConfirmationScreenViewModelTests {
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
// When continuing from the confirmation screen. // When continuing from the confirmation screen.
let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOAuth }
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
@@ -246,7 +246,7 @@ final class ServerConfirmationScreenViewModelTests {
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.prompt == .consent) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesReceivedArguments?.prompt == .consent)
#expect(service.homeserver.value.loginMode == .oidc(supportsCreatePrompt: true)) #expect(service.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: true))
} }
@Test @Test
@@ -257,13 +257,13 @@ final class ServerConfirmationScreenViewModelTests {
Issue.record("The configuration should succeed.") Issue.record("The configuration should succeed.")
return return
} }
#expect(service.homeserver.value.loginMode == .oidc(supportsCreatePrompt: true)) #expect(service.homeserver.value.loginMode == .oAuth(supportsCreatePrompt: true))
#expect(context.viewState.mode == .picker(appSettings.accountProviders)) #expect(context.viewState.mode == .picker(appSettings.accountProviders))
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
// When continuing from the confirmation screen. // When continuing from the confirmation screen.
let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOIDC } let deferred = deferFulfillment(viewModel.actions) { $0.isContinueWithOAuth }
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
@@ -275,8 +275,8 @@ final class ServerConfirmationScreenViewModelTests {
@Test @Test
func pickerForPasswordLoginWithoutConfiguration() async throws { func pickerForPasswordLoginWithoutConfiguration() async throws {
// Given a view model for login using a service that hasn't been configured (against a server that doesn't support OIDC). // Given a view model for login using a service that hasn't been configured (against a server that doesn't support OAuth).
setupViewModel(authenticationFlow: .login, supportsOIDC: false, restrictedFlow: true) setupViewModel(authenticationFlow: .login, supportsOAuth: false, restrictedFlow: true)
#expect(service.homeserver.value.loginMode == .unknown) #expect(service.homeserver.value.loginMode == .unknown)
#expect(context.viewState.mode == .picker(appSettings.accountProviders)) #expect(context.viewState.mode == .picker(appSettings.accountProviders))
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 0)
@@ -287,7 +287,7 @@ final class ServerConfirmationScreenViewModelTests {
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
// Then a call to configure service should be made, but not for the OIDC URL. // Then a call to configure service should be made, but not for the OAuth URL.
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
#expect(service.homeserver.value.loginMode == .password) #expect(service.homeserver.value.loginMode == .password)
@@ -296,7 +296,7 @@ final class ServerConfirmationScreenViewModelTests {
@Test @Test
func pickerForPasswordLoginAfterConfiguration() async throws { func pickerForPasswordLoginAfterConfiguration() async throws {
// Given a view model for login using a service that has already been configured (via the server selection screen). // Given a view model for login using a service that has already been configured (via the server selection screen).
setupViewModel(authenticationFlow: .login, supportsOIDC: false, restrictedFlow: true) setupViewModel(authenticationFlow: .login, supportsOAuth: false, restrictedFlow: true)
guard case .success = await service.configure(for: appSettings.accountProviders[0], flow: .login) else { guard case .success = await service.configure(for: appSettings.accountProviders[0], flow: .login) else {
Issue.record("The configuration should succeed.") Issue.record("The configuration should succeed.")
return return
@@ -311,7 +311,7 @@ final class ServerConfirmationScreenViewModelTests {
context.send(viewAction: .confirm) context.send(viewAction: .confirm)
try await deferred.fulfill() try await deferred.fulfill()
// Then the configured homeserver should be used and no additional client should be built, nor a call to get the OIDC URL. // Then the configured homeserver should be used and no additional client should be built, nor a call to get the OAuth URL.
#expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1) #expect(clientFactory.makeClientHomeserverAddressSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount == 1)
#expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0) #expect(client.urlForOauthOauthConfigurationPromptLoginHintDeviceIdAdditionalScopesCallsCount == 0)
} }
@@ -319,8 +319,8 @@ final class ServerConfirmationScreenViewModelTests {
// MARK: - Helpers // MARK: - Helpers
private func setupViewModel(authenticationFlow: AuthenticationFlow, private func setupViewModel(authenticationFlow: AuthenticationFlow,
supportsOIDC: Bool = true, supportsOAuth: Bool = true,
supportsOIDCCreatePrompt: Bool = true, supportsOAuthCreatePrompt: Bool = true,
supportsPasswordLogin: Bool = true, supportsPasswordLogin: Bool = true,
restrictedFlow: Bool = false, restrictedFlow: Bool = false,
requiresElementPro: Bool = false) { requiresElementPro: Bool = false) {
@@ -330,7 +330,7 @@ final class ServerConfirmationScreenViewModelTests {
allowOtherAccountProviders: false, allowOtherAccountProviders: false,
hideBrandChrome: false, hideBrandChrome: false,
pushGatewayBaseURL: appSettings.pushGatewayBaseURL, pushGatewayBaseURL: appSettings.pushGatewayBaseURL,
oidcRedirectURL: appSettings.oidcRedirectURL, oAuthRedirectURL: appSettings.oAuthRedirectURL,
websiteURL: appSettings.websiteURL, websiteURL: appSettings.websiteURL,
logoURL: appSettings.logoURL, logoURL: appSettings.logoURL,
copyrightURL: appSettings.copyrightURL, copyrightURL: appSettings.copyrightURL,
@@ -350,8 +350,8 @@ final class ServerConfirmationScreenViewModelTests {
} }
// Manually create a configuration as the default homeserver address setting is immutable. // Manually create a configuration as the default homeserver address setting is immutable.
client = ClientSDKMock(configuration: .init(oidcLoginURL: supportsOIDC ? "https://account.matrix.org/authorize" : nil, client = ClientSDKMock(configuration: .init(oAuthLoginURL: supportsOAuth ? "https://account.matrix.org/authorize" : nil,
supportsOIDCCreatePrompt: supportsOIDCCreatePrompt, supportsOAuthCreatePrompt: supportsOAuthCreatePrompt,
supportsPasswordLogin: supportsPasswordLogin, supportsPasswordLogin: supportsPasswordLogin,
elementWellKnown: requiresElementPro ? "{\"version\":1,\"enforce_element_pro\":true}" : nil)) elementWellKnown: requiresElementPro ? "{\"version\":1,\"enforce_element_pro\":true}" : nil))
let configuration = AuthenticationClientFactoryMock.Configuration(homeserverClients: ["matrix.org": client]) let configuration = AuthenticationClientFactoryMock.Configuration(homeserverClients: ["matrix.org": client])
@@ -370,7 +370,7 @@ final class ServerConfirmationScreenViewModelTests {
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
// Add a fake window in order for the OIDC flow to continue // Add a fake window in order for the OAuth flow to continue
viewModel.context.send(viewAction: .updateWindow(UIWindow())) viewModel.context.send(viewAction: .updateWindow(UIWindow()))
} }
} }
@@ -387,9 +387,9 @@ private extension ServerConfirmationScreenViewState {
} }
private extension ServerConfirmationScreenViewModelAction { private extension ServerConfirmationScreenViewModelAction {
var isContinueWithOIDC: Bool { var isContinueWithOAuth: Bool {
switch self { switch self {
case .continueWithOIDC: true case .continueWithOAuth: true
default: false default: false
} }
} }

View File

@@ -45,7 +45,7 @@ struct SoftLogoutScreenViewModelTests {
} }
@Test @Test
func initialStateForOIDC() { func initialStateForOAuth() {
let viewModel = SoftLogoutScreenViewModel(credentials: credentials, let viewModel = SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg, homeserver: .mockMatrixDotOrg,
keyBackupNeeded: false) keyBackupNeeded: false)
@@ -54,7 +54,7 @@ struct SoftLogoutScreenViewModelTests {
// Given a view model where the user hasn't yet sent the verification email. // Given a view model where the user hasn't yet sent the verification email.
#expect(context.password.isEmpty, "The view model should start with an empty password.") #expect(context.password.isEmpty, "The view model should start with an empty password.")
#expect(!context.viewState.canSubmit, "The view model should start with an invalid password.") #expect(!context.viewState.canSubmit, "The view model should start with an invalid password.")
#expect(context.viewState.loginMode.supportsOIDCFlow, "The view model should show OIDC button for the given homeserver.") #expect(context.viewState.loginMode.supportsOAuthFlow, "The view model should show OAuth button for the given homeserver.")
#expect(!context.viewState.showRecoverEncryptionKeysMessage, "The view model should not show recover encryption keys message.") #expect(!context.viewState.showRecoverEncryptionKeysMessage, "The view model should not show recover encryption keys message.")
} }