@@ -171,19 +171,14 @@ public function testAuthorizeWithoutApprovedClient()
171171 }
172172
173173 /**
174- * @testdox ServerController should return a 302 redirect when asked to authorize client that has been approved
174+ * @testdox ServerController should return a 400 when asked to authorize a client that sends an incorrect redirect URI
175175 *
176176 * @covers ::authorize
177177 */
178- public function testAuthorizeWithApprovedClient ()
178+ public function testAuthorizeWithInvalidRedirectUri ()
179179 {
180180 $ _GET ['client_id ' ] = self ::MOCK_CLIENT_ID ;
181- $ _GET ['nonce ' ] = 'mock-nonce ' ;
182- // JWT with empty payload, HS256 encoded, created with `private.key` from fixtures
183- $ _GET ['request ' ] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.8VKCTiBegJPuPIZlp0wbV0Sbdn5BS6TE5DCx6oYNc5o ' ;
184- $ _GET ['response_type ' ] = 'mock-response-type ' ;
185-
186- $ _SERVER ['REQUEST_URI ' ] = 'https://mock.server ' ;
181+ $ _GET ['redirect_uri ' ] = 'https://some.other.client/redirect ' ;
187182
188183 $ clientData = json_encode (['client_name ' => 'Mock Client ' , 'redirect_uris ' => ['https://mock.client/redirect ' ]]);
189184
@@ -200,15 +195,15 @@ public function testAuthorizeWithApprovedClient()
200195 $ response = $ controller ->authorize ();
201196
202197 $ expected = [
203- 'data ' => 'ok ' ,
198+ 'data ' => 'Provided redirect URI does not match any registered URIs ' ,
204199 'headers ' => [
205200 'Cache-Control ' => 'no-cache, no-store, must-revalidate ' ,
206201 'Content-Security-Policy ' => "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none' " ,
207202 'Content-Type ' => 'application/json; charset=utf-8 ' ,
208203 'Feature-Policy ' => "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none' " ,
209204 'X-Robots-Tag ' => 'noindex, nofollow ' ,
210205 ],
211- 'status ' => Http::STATUS_FOUND ,
206+ 'status ' => Http::STATUS_BAD_REQUEST ,
212207 ];
213208
214209 $ actual = [
@@ -217,32 +212,87 @@ public function testAuthorizeWithApprovedClient()
217212 'status ' => $ response ->getStatus (),
218213 ];
219214
220- $ location = $ actual ['headers ' ]['Location ' ];
221-
222215 // Not comparing time-sensitive data
223- unset($ actual ['headers ' ]['X-Request-Id ' ], $ actual [ ' headers ' ][ ' Location ' ] );
216+ unset($ actual ['headers ' ]['X-Request-Id ' ]);
224217
225218 $ this ->assertEquals ($ expected , $ actual );
226-
227- // @TODO: Move $location assert to a separate test
228- $ url = parse_url ($ location );
229-
230- parse_str ($ url ['fragment ' ], $ url ['fragment ' ]);
231-
232- unset($ url ['fragment ' ]['access_token ' ], $ url ['fragment ' ]['id_token ' ]);
233-
234- $ this ->assertEquals ([
235- 'scheme ' => 'https ' ,
236- 'host ' => 'mock.client ' ,
237- 'path ' => '/redirect ' ,
238- 'fragment ' => [
239- 'token_type ' => 'Bearer ' ,
240- 'expires_in ' => '3600 ' ,
241- ],
242- ], $ url );
243219 }
244220
245- /**
221+ /**
222+ * @testdox ServerController should return a 302 redirect when asked to authorize client that has been approved
223+ *
224+ * @covers ::authorize
225+ */
226+ public function testAuthorize ()
227+ {
228+ $ _GET ['client_id ' ] = self ::MOCK_CLIENT_ID ;
229+ $ _GET ['nonce ' ] = 'mock-nonce ' ;
230+ // JWT with empty payload, HS256 encoded, created with `private.key` from fixtures
231+ $ _GET ['request ' ] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.8VKCTiBegJPuPIZlp0wbV0Sbdn5BS6TE5DCx6oYNc5o ' ;
232+ $ _GET ['response_type ' ] = 'mock-response-type ' ;
233+ $ _GET ['redirect_uri ' ] = 'https://mock.client/redirect ' ;
234+
235+ $ _SERVER ['REQUEST_URI ' ] = 'https://mock.server ' ;
236+ $ _SERVER ['REQUEST_URI ' ];
237+
238+ $ clientData = json_encode (['client_name ' => 'Mock Client ' , 'redirect_uris ' => ['https://mock.client/redirect ' ]]);
239+
240+ $ parameters = $ this ->createMockConstructorParameters ($ clientData );
241+
242+ $ this ->mockConfig ->method ('getUserValue ' )
243+ ->with (self ::MOCK_USER_ID , Application::APP_ID , 'allowedClients ' , '[] ' )
244+ ->willReturn (json_encode ([self ::MOCK_CLIENT_ID ]));
245+
246+ $ this ->mockUserManager ->method ('userExists ' )->willReturn (true );
247+
248+ $ controller = new ServerController (...$ parameters );
249+
250+ $ response = $ controller ->authorize ();
251+
252+ $ expected = [
253+ 'data ' => 'ok ' ,
254+ 'headers ' => [
255+ 'Cache-Control ' => 'no-cache, no-store, must-revalidate ' ,
256+ 'Content-Security-Policy ' => "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none' " ,
257+ 'Content-Type ' => 'application/json; charset=utf-8 ' ,
258+ 'Feature-Policy ' => "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none' " ,
259+ 'X-Robots-Tag ' => 'noindex, nofollow ' ,
260+ ],
261+ 'status ' => Http::STATUS_FOUND ,
262+ ];
263+
264+ $ actual = [
265+ 'data ' => $ response ->getData (),
266+ 'headers ' => $ response ->getHeaders (),
267+ 'status ' => $ response ->getStatus (),
268+ ];
269+
270+ $ location = $ actual ['headers ' ]['Location ' ] ?? '' ;
271+
272+ // Not comparing time-sensitive data
273+ unset($ actual ['headers ' ]['X-Request-Id ' ], $ actual ['headers ' ]['Location ' ]);
274+
275+ $ this ->assertEquals ($ expected , $ actual );
276+
277+ // @TODO: Move $location assert to a separate test
278+ $ url = parse_url ($ location );
279+
280+ parse_str ($ url ['fragment ' ], $ url ['fragment ' ]);
281+
282+ unset($ url ['fragment ' ]['access_token ' ], $ url ['fragment ' ]['id_token ' ]);
283+
284+ $ this ->assertEquals ([
285+ 'scheme ' => 'https ' ,
286+ 'host ' => 'mock.client ' ,
287+ 'path ' => '/redirect ' ,
288+ 'fragment ' => [
289+ 'token_type ' => 'Bearer ' ,
290+ 'expires_in ' => '3600 ' ,
291+ ],
292+ ], $ url );
293+ }
294+
295+ /**
246296 * @testdox ServerController should return a 400 when asked to register without valid client data
247297 *
248298 * @covers ::register
0 commit comments