Skip to content

Commit d7659d5

Browse files
authored
Merge pull request #282 from cakephp/issue-281
Only include path + query in redirect targets
2 parents 9db5b0f + de1dc2a commit d7659d5

File tree

3 files changed

+69
-11
lines changed

3 files changed

+69
-11
lines changed

docs/en/migration-from-the-authcomponent.rst

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ using the ``queryParam`` option::
208208

209209
// Add the authentication middleware
210210
$authentication = new AuthenticationMiddleware($this, [
211-
'unauthenticatedRedirect' => Router::url('users:login'),
211+
'unauthenticatedRedirect' => '/users/login',
212212
'queryParam' => 'redirect',
213213
]);
214214

@@ -218,6 +218,30 @@ using the ``queryParam`` option::
218218
return $middlewareQueue;
219219
}
220220

221+
Then in your controller's login method you can use the redirect query parameter::
222+
223+
public function login()
224+
{
225+
$result = $this->Authentication->getResult();
226+
227+
// Regardless of POST or GET, redirect if user is logged in
228+
if ($result->isValid()) {
229+
// Use the redirect parameter if present. Only use the path
230+
// and query segments to prevent an open redirect.
231+
if ($this->request->getQuery('redirect')) {
232+
$parsed = parse_url($this->request->getQuery('redirect'));
233+
if ($parsed === false) {
234+
$parsed = ['path' => '/', 'query' => ''];
235+
}
236+
$parsed += ['path' => '/', 'query' => ''];
237+
$redirect = $parsed['path'] . '?' . $parsed['query'];
238+
} else {
239+
$redirect = ['controller' => 'Pages', 'action' => 'display', 'home'];
240+
}
241+
return $this->redirect($redirect);
242+
}
243+
}
244+
221245
Migrating Hashing Upgrade Logic
222246
===============================
223247

@@ -241,10 +265,11 @@ leveraging the ``AuthenticationService``::
241265
$this->Users->save($user);
242266
}
243267

244-
$redirect = $this->request->getQuery(
245-
'redirect',
246-
['controller' => 'Pages', 'action' => 'display', 'home']
247-
);
248-
return $this->redirect($redirect);
268+
// Redirect to a logged in page
269+
return $this->redirect([
270+
'controller' => 'Pages',
271+
'action' => 'display',
272+
'home'
273+
]);
249274
}
250275
}

src/Middleware/AuthenticationMiddleware.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ protected function getRedirectUrl($target, ServerRequestInterface $request)
150150
if (property_exists($uri, 'base')) {
151151
$uri = $uri->withPath($uri->base . $uri->getPath());
152152
}
153-
$query = urlencode($param) . '=' . urlencode($uri);
153+
$redirect = $uri->getPath();
154+
if ($uri->getQuery()) {
155+
$redirect .= '?' . $uri->getQuery();
156+
}
157+
$query = urlencode($param) . '=' . urlencode($redirect);
154158

155159
$url = parse_url($target);
156160
if (isset($url['query']) && strlen($url['query'])) {

tests/TestCase/Middleware/AuthenticationMiddlewareTest.php

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ public function testUnauthenticatedRedirectWithQuery()
469469

470470
$response = $middleware($request, $response, $next);
471471
$this->assertSame(302, $response->getStatusCode());
472-
$this->assertSame('/users/login?redirect=http%3A%2F%2Flocalhost%2Ftestpath', $response->getHeaderLine('Location'));
472+
$this->assertSame('/users/login?redirect=%2Ftestpath', $response->getHeaderLine('Location'));
473473
$this->assertSame('', $response->getBody() . '');
474474
}
475475

@@ -498,7 +498,7 @@ public function testUnauthenticatedRedirectWithExistingQuery()
498498

499499
$response = $middleware($request, $response, $next);
500500
$this->assertSame(302, $response->getStatusCode());
501-
$this->assertSame('/users/login?hello=world&redirect=http%3A%2F%2Flocalhost%2Ftestpath', $response->getHeaderLine('Location'));
501+
$this->assertSame('/users/login?hello=world&redirect=%2Ftestpath', $response->getHeaderLine('Location'));
502502
$this->assertSame('', $response->getBody() . '');
503503
}
504504

@@ -528,7 +528,7 @@ public function testUnauthenticatedRedirectWithFragment()
528528
$response = $middleware($request, $response, $next);
529529
$this->assertSame(302, $response->getStatusCode());
530530
$this->assertSame(
531-
'/users/login?hello=world&redirect=http%3A%2F%2Flocalhost%2Ftestpath#frag',
531+
'/users/login?hello=world&redirect=%2Ftestpath#frag',
532532
$response->getHeaderLine('Location')
533533
);
534534
$this->assertSame('', $response->getBody() . '');
@@ -563,7 +563,36 @@ public function testUnauthenticatedRedirectWithBase()
563563

564564
$response = $middleware($request, $response, $next);
565565
$this->assertSame(302, $response->getStatusCode());
566-
$this->assertSame('/users/login?redirect=http%3A%2F%2Flocalhost%2Fbase%2Ftestpath', $response->getHeaderLine('Location'));
566+
$this->assertSame('/users/login?redirect=%2Fbase%2Ftestpath', $response->getHeaderLine('Location'));
567+
$this->assertSame('', $response->getBody() . '');
568+
}
569+
570+
/**
571+
* test unauthenticated redirects preserving path and query
572+
*
573+
* @return void
574+
*/
575+
public function testUnauthenticatedRedirectWithQueryStringData()
576+
{
577+
$request = ServerRequestFactory::fromGlobals(
578+
['REQUEST_URI' => '/testpath', 'QUERY_STRING' => 'a=b&c=d'],
579+
[],
580+
['username' => 'mariano', 'password' => 'password']
581+
);
582+
$response = new Response();
583+
584+
$middleware = new AuthenticationMiddleware($this->service, [
585+
'unauthenticatedRedirect' => '/users/login',
586+
'queryParam' => 'redirect',
587+
]);
588+
589+
$next = function ($request, $response) {
590+
throw new UnauthenticatedException();
591+
};
592+
593+
$response = $middleware($request, $response, $next);
594+
$this->assertSame(302, $response->getStatusCode());
595+
$this->assertSame('/users/login?redirect=%2Ftestpath%3Fa%3Db%26c%3Dd', $response->getHeaderLine('Location'));
567596
$this->assertSame('', $response->getBody() . '');
568597
}
569598

0 commit comments

Comments
 (0)