@@ -116,7 +116,26 @@ public function testLoginWithEmptyCaptcha()
116116 $ subject ->login ('' , '' );
117117 }
118118
119- public function testLoginWithOTPEnabledAndValidCredentialsReturnsFalse (): void
119+ public function testLoginWithValidCaptchaAndValidCredentials (): void
120+ {
121+ $ this ->disableTwoFactorAuth ();
122+
123+ $ this ->requestMock
124+ ->method ('getRequestParameter ' )
125+ ->willReturnCallback (function ($ param ) {
126+ if ($ param === 'captcha ' ) {
127+ return 'valid_captcha ' ;
128+ }
129+ return null ;
130+ });
131+
132+ $ subject = oxNew (User::class);
133+ $ result = $ subject ->login (self ::OTP_USER_NAME , self ::OTP_USER_PASSWORD );
134+
135+ $ this ->assertTrue ($ result );
136+ }
137+
138+ public function testLoginWithOTPEnabledAndValidCredentialsRedirectsToOTP (): void
120139 {
121140 $ this ->disableCaptcha ();
122141 $ this ->enableTwoFactorAuth ();
@@ -126,11 +145,9 @@ public function testLoginWithOTPEnabledAndValidCredentialsReturnsFalse(): void
126145 Registry::set (Utils::class, $ utilsMock );
127146
128147 $ subject = oxNew (User::class);
129- $ result = $ subject ->login (self ::OTP_USER_NAME , self ::OTP_USER_PASSWORD );
148+ $ subject ->login (self ::OTP_USER_NAME , self ::OTP_USER_PASSWORD );
130149
131- $ this ->assertFalse ($ result );
132- $ this ->assertEquals (
133- self ::OTP_USER_NAME ,
150+ $ this ->assertNotNull (
134151 Registry::getSession ()->getVariable (AuthorizeService::USER_SESSION_KEY )
135152 );
136153 }
@@ -173,20 +190,74 @@ public function testLoginWithOTPDisabledCallsParentLogin(): void
173190 );
174191 }
175192
176- public function testLoginWithOTPEnabledStoresUserInSession (): void
193+ public function testLoginWithOTPEnabledStoresUserIdInSession (): void
194+ {
195+ $ this ->disableCaptcha ();
196+ $ this ->enableTwoFactorAuth ();
197+
198+ $ utilsMock = $ this ->createMock (Utils::class);
199+ $ utilsMock ->method ('redirect ' );
200+ Registry::set (Utils::class, $ utilsMock );
201+
202+ $ subject = oxNew (User::class);
203+ $ subject ->login (self ::OTP_USER_NAME , self ::OTP_USER_PASSWORD );
204+
205+ $ sessionUserId = Registry::getSession ()->getVariable (AuthorizeService::USER_SESSION_KEY );
206+ $ this ->assertNotNull ($ sessionUserId );
207+ $ this ->assertEquals ($ subject ->getId (), $ sessionUserId );
208+ }
209+
210+ public function testLoginWithOTPPassSessionVariableSkipsOTPRedirect (): void
211+ {
212+ $ this ->disableCaptcha ();
213+ $ this ->enableTwoFactorAuth ();
214+
215+ $ subject = oxNew (User::class);
216+ $ subject ->load ($ this ->getOTPUserId ());
217+
218+ Registry::getSession ()->setVariable ('OTP_PASS ' , $ subject ->getId ());
219+
220+ $ utilsMock = $ this ->createMock (Utils::class);
221+ $ utilsMock ->expects ($ this ->never ())->method ('redirect ' );
222+ Registry::set (Utils::class, $ utilsMock );
223+
224+ $ result = $ subject ->login (self ::OTP_USER_NAME , self ::OTP_USER_PASSWORD );
225+
226+ $ this ->assertTrue ($ result );
227+ $ this ->assertNull (
228+ Registry::getSession ()->getVariable ('OTP_PASS ' ),
229+ 'OTP_PASS session variable should be cleared after successful login '
230+ );
231+ $ this ->assertNull (
232+ Registry::getSession ()->getVariable (AuthorizeService::USER_SESSION_KEY ),
233+ 'USER_SESSION_KEY should not be set when OTP was already validated '
234+ );
235+ }
236+
237+ public function testLoginWithOTPPassSessionVariableMismatchTriggersOTPFlow (): void
177238 {
178239 $ this ->disableCaptcha ();
179240 $ this ->enableTwoFactorAuth ();
180241
242+ $ mismatchedUserId = 'different-user-id ' ;
243+ Registry::getSession ()->setVariable ('OTP_PASS ' , $ mismatchedUserId );
244+
181245 $ utilsMock = $ this ->createMock (Utils::class);
182246 $ utilsMock ->method ('redirect ' );
183247 Registry::set (Utils::class, $ utilsMock );
184248
185249 $ subject = oxNew (User::class);
186250 $ subject ->login (self ::OTP_USER_NAME , self ::OTP_USER_PASSWORD );
187251
188- $ sessionUserName = Registry::getSession ()->getVariable (AuthorizeService::USER_SESSION_KEY );
189- $ this ->assertEquals (self ::OTP_USER_NAME , $ sessionUserName );
252+ $ this ->assertNotNull (
253+ Registry::getSession ()->getVariable (AuthorizeService::USER_SESSION_KEY ),
254+ 'USER_SESSION_KEY should be set when OTP flow is triggered '
255+ );
256+ $ this ->assertSame (
257+ $ mismatchedUserId ,
258+ Registry::getSession ()->getVariable ('OTP_PASS ' ),
259+ 'OTP_PASS should NOT be cleared when user ID does not match '
260+ );
190261 }
191262
192263 private function enableTwoFactorAuth (): void
@@ -219,4 +290,11 @@ private function disableCaptcha(): void
219290 $ captchaSettings = ContainerFacade::get (CaptchaSettingsServiceInterface::class);
220291 $ captchaSettings ->saveIsCaptchaEnabled (false );
221292 }
293+
294+ private function getOTPUserId (): string
295+ {
296+ $ user = oxNew (User::class);
297+ $ user ->load ($ user ->getIdByUserName (self ::OTP_USER_NAME ));
298+ return $ user ->getId ();
299+ }
222300}
0 commit comments