I have a controller called Accounts, with the views signin and signout.
The corresponding functions look like this:
function signin()
{
if (!empt
http://book.cakephp.org/view/430/referer
Use a hidden <input>
field that holds the initial referrer and gets submitted with the login data.
CakePHP 2.x here
public function beforeFilter() {
// redirect url
if($this->request->here!= '/users/login') {
$user_id = AuthComponent::user('id');
if(empty($user_id)) { $this->Session->write('redirect_url_after_login', Router::url($this->request->here, true)); }
}
This will store the url the user wanted to go before request, only if the url is not /users/login (replace with your url of login) AND if no user is logged.
$redirect_url_after_login = $this->Session->read('redirect_url_after_login');
if(!empty($redirect_url_after_login))
echo $this->Form->input('redirect_url_after_login', ['value'=>$redirect_url_after_login, 'type'=>'hidden']);
public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$redirect_url_after_login = $this->request->data['User']['redirect_url_after_login'];
if(!empty($redirect_url_after_login)
&&filter_var($redirect_url_after_login, FILTER_VALIDATE_URL)
&&parse_url($redirect_url_after_login, PHP_URL_HOST)==$_SERVER['HTTP_HOST'])
return $this->redirect($redirect_url_after_login);
$this->Session->delete('redirect_url_after_login');
return $this->redirect($this->Auth->redirect());
}
I added a couple of security checks, like "is the redirect url a valid url?" and "is it redirecting towards my domain or an external domain?".
Note: I know checking $_SERVER['HTTP_HOST']
is not bulletproof, but here we're talking about preventing open redirect vulnerability, so it's enough.
I don't know about the best way, but I store the attempted destination in a session variable before redirecting them to the sign in page.
Once they have signed in, I redirect them to the stored destination.
Use the AppController and UsersController to set it up
In AppController beforeFilter action
$referer = Router::url($this->url, true);
$this->Auth->loginAction = array('controller'=>'users','action'=>'login','?'=>['referer'=>$referer]);
In UsersController login action
if($this->Auth->login())
{
$this->Session->setFlash(__('Logged in successfully !'));
$redirect = $this->request->query('referer');
if(!empty($redirect))
{
return $this->redirect($this->Auth->redirectUrl($redirect));
}else{
return $this->redirect($this->Auth->redirectUrl());
}
}
the best way is to use cakes Auth component and let it do what it does... http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#AuthComponent::$loginRedirect
Here's how I do it with referer
I have 2 login forms, one is a form at the top of all pages for easily sign in, the other is on login action. If the user comes in using the form at the top, the form submission then goes to login page and you can use $this->referer()
to redirect the user back.
But the problem is that, if the user types the password wrong or enter invalid credential, he will then end up on the login page. If he then enters the right username+password, and redirection occurs using $this->referer()
, which in this case is itself. The user then could then either 1. get redirected back to login page again or 2. even worse can get stuck in an infinite loop b/c login will keep redirecting to itself.
So, I add some logic to check to make sure that referer is not login page. Also, I add another logic to store $this->referer()
the first time the user lands on login page, we we know where exactly the page before login page.
To store the page before login page, put this code at the end of the action (login view rendering is about to begin)
//get the current url of the login page (or current controller+action)
$currentLoginUrl = strtolower( "/" .$this->name ."/" .$this->action );
if( $this->referer() != $currentLoginUrl )
{
//store this value to use once user is succussfully logged in
$this->Session->write('beforeLogin_referer', $this->referer($this->Auth->redirect(), true)) ) ; //if referer can't be read, or if its not from local server, use $this->Auth->rediret() instead
}
Now put the code to redirect in the part of the code where authentication is succesful (or in if( $this->Auth->user() ){ }
):
//get the login page url again (by gettting its controller, or plural of Model, and this current page action and make the url)
$currentLoginUrl = strtolower( "/" .$this->name ."/" .$this->action );
//if the referer page is not from login page,
if( $this->referer() != $currentLoginUrl )
{
//use $this->referer() right away
$this->redirect($this->referer($this->Auth->redirect(), true)); //if referer can't be read, or if its not from local server, use $this->Auth->rediret() instead
}
else
{
//if the user lands on login page first, rely on our session
$this->redirect( $this->Session->read('beforeLogin_referer') );
}
Hope this works for you.