419 CSRF Token Mismatch after logging out and logging back in - csrf

After I log out and want to log back in I get the following error:
message: "CSRF token mismatch.", exception:
"Symfony\Component\HttpKernel\Exception\HttpException"
I customized some parts of the LoginController so it might have something to do with that. I tried searching in the unmodified LoginController but I can't find anything. My login function from my vuex store:
login({ commit }, user) {
return new Promise((resolve, reject) => {
axios({ url: '/login', data: user, method: 'POST' })
.then(response => {
const user = response.data.user
commit('auth_success', user)
resolve(response)
})
.catch(error => {
commit('auth_error')
reject(error)
})
})
},
My logout function, also from my vuex store:
logout({ commit }) {
return new Promise((resolve, reject) => {
axios({ url: '/logout', method: 'POST' })
.then(() => {
commit('logout')
localStorage.removeItem('user');
resolve()
})
.catch(error => {
reject(error)
})
})
},
And this is my LoginController:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = RouteServiceProvider::HOME;
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function login(Request $request)
{
if ($this->attemptLogin($request)) {
$request->session()->regenerate();
return $this->authenticated($request, $this->guard()->user());
}
}
protected function attemptLogin(Request $request)
{
return $this->guard()->attempt(['email' => $request->json('email'), 'password' => $request->json('password')]);
}
protected function authenticated(Request $request, $user)
{
if ($user) {
return response()->json([
'user' => $user
], 200);
} else {
return response('', 400);
}
}
public function logout(Request $request)
{
$this->guard()->logout();
if ($request->session()->invalidate()) {
return response('', 200);
}
}
}
Am I missing something?
Update:
If I do a page refresh, I don't get the error anymore. This has probably something to do with that I have a Single Page Application using vue-router. So I guess I need a page refresh to avoid the 419 error. But this is not an ideal situation. Does someone has experience with this?

In a SAP, your browser still holds all of your JS variables that you do not specifically clear. It sounds like when you log out of your SAP you are generating a new CSRF token, but not clearing your JS variables. The CSRF protection then checks to see if you already have a CSRF token, when it finds it, and sees a mismatch, it assumes you are attempting a CSRF attack and blocks you. In general, it is always best to force a page reload on a logout to clear your JS variables since not doing this risks the next user being able to see all sorts of other should be private data you may have forgotten to clear.
Responding with with the following JS at the end your log out procedure will trigger a refresh on the user's browser automatically.
// Reloads the current URL.
location.reload();
If you absolutely don't want to do a page reload, you will need to clear all of your local data, not just the 'user' object. The best way to do this that I know of is as follows:
// Clear all JS variables for the current domain.
localStorage.clear();
sessionStorage.clear();
On a logout, I personally prefer doing a reload because it also prompts the cache to revalidate non-JS local data as well which can prevent certain other kinds of leeks like showing the new user images from the last user's session.

Related

Yii2 Redirect to previous page after update

I have a application where after update user should be redirected to previous page from pagination.
let's say there is a gridview and user is at page 3. Then he update some record at that page. There should be a redirect to index page 3. What if, while user is updating record, before save, he opens another controller/action in new tab. Then ReturnUrl is now that new action and after save the record he is updating, he is redirected to that new url.
I've tried to set in every action "index" Url::remember(); and then in action "update" - return $this->goBack().
Also return $this->redirect(Yii::$app->request->referrer);, but it stays at same page.
There is a way to store every index URL in session, but in large project that means many sessions.
You could provide the returnUrl to the link, say:
Url::to(['update','id'=>$model->url,'returnUrl'=> Yii::$app->request->url]);
Then in your controller, use $this->request->queryParams['returnUrl'] to redirect to the previousUrl.
To take it one step further, to always provide the returnUrl, you could extend the Url Helper class:
namespace app\helpers;
use yii\helpers;
class Url extends yii\helpers\Url
public function toRouteAndReturn($route, array $params = [], $scheme = false) {
$params['returnUrl'] = Yii::$app->request->url;
return parent::toRoute($route,$params,$scheme);
}
You could provide in your main config:
'on afterAction' => function($event) {
if(!Yii::$app->getResponse()->isSent && !empty(Yii::$app->getRequest()->queryParams['returnUrl']) {
Yii::$app->getResponse()->redirect(Yii::$app->getRequest()->queryParams['returnUrl']);
}
}
Then you could use app\helpers\Url::toRouteAndReturn() instead of yii\helpers\Url::toRoute() to have it return to the previous url.
You can try below Solution.
First in your index page, get current page url and encode it.
$current_url=base64_encode(\Yii::$app->request->getUrl());
Append this url with your update link as below.
'urlCreator' => function ($action, $model, $key, $index) use ($current_url) {
if ($action === 'update') {
$url = Yii::$app->request->baseUrl . '/controllerName/update?id=' . $model->id.'&prev='.$current_url;
return $url;
}
// ......
}
In Controller, in Update method decode url as below and use for redirection.
public function actionUpdate($id)
{
$model = $this->findModel($id);
$prev=base64_decode($_REQUEST['prev']);
// ......
return $this->redirect($prev); // you will redirect from where update method is called
// ......
}
Isn't it quite easy to pass page param into your update url (<model/update>) like <model>/update?id=<id>&page=<page>?
in your index.php view, edit your ActionColumn as follow:
[
'class' => 'yii\grid\ActionColumn',
'urlCreator' => function ($action, $model, $key, $index) {
return \yii\helpers\Url::to([$action, 'id' => $model->id, 'page' => Yii::$app->request->getQueryParam('page', null)]);
},
],
As you can see, I'm getting page param from request url and pass it to models' action buttons
And when you click to update model, the page that we entered from is stored/placed in url.
Controller:
public function actionUpdate($id, $page = null)
{
$model = $this->findModel($id);
...
if($model->save()) {
return $this->redirect(['index', 'page' => $page]);
}
...
}
Finally, after we successfully update the model, the action redirects us to previous index page.

How can I replace this _forward() with something that can exit?

I use the following code over and over in my zend framework application. It is used in action() to check if an article exists. If not, the user shall see an error message:
$article = ArticleQuery::create()->findOneByUrl($this->_getParam('url', ''));
if (!$article) {
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
return $this->_forward('error', null, null, array(
'message' => 'Article not found',
));
}
I was wondering how to factor this out into an own method to reduce the code load in all actions.
I came to something like this:
protected function myAction() {
$article = $this->getArticleIfExists($this->_getParam('url', ''));
if ($article == null) {
return;
}
}
protected function getArticleIfExists($url) {
$article = ArticleQuery::create()->findOneByUrl($this->_getParam('url', ''));
if ($article) {
return $article;
} else {
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
$this->_forward('error', null, null, array(
'message' => 'Article not found',
));
return nulL;
}
}
I still would like to get rid of the if case in myAction(), but _forward() does not allow to exit the execution (of course, because it still needs to execute the other actions).
Another possibility (I have implemented in some other controllers) is this:
protected function myAction() {
$article = ArticleQuery::create()->findOneByUrl($this->_getParam('url', ''));
if (!$article) {
return $this->notFound('Article does not exist');
}
}
protected function notFound($message) {
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
return $this->_forward('error', null, null, array(
'message' => $message,
));
}
Again we have this if check in the action. It’s already better than before, but can we make it even better?
How can I circumvent this? Is there a possibility to do it without losing the current URL? With a Redirector I can of course exit, but then I would lose the current URL (/controller/myaction/url/hello -> /error/error/message/Article%20not%20found)
A possible approach would be to throw an Exception. Because of the Zend_Controller_Plugin_ErrorHandler this will automatically redirect you to the ErrorController without any further code being executed.
If you don't want to get to the ErrorController but only to the current controller's error actions, you can simply modify the plugin in the controller's init method:
public function init()
{
$plugin = Zend_Controller_Front::getInstance()->getPlugin('Zend_Controller_Plugin_ErrorHandler');
$plugin->setErrorHandlerModule('default')
->setErrorHandlerController('MyController')
->setErrorHandlerAction('error');
}
But of course you can also write your own ErrorHandler plugin for a more fine grained error handling. This is described in the Zend Framework Manual on Plugins
For something as simple as just displaying a "* does not exist" against a user request I prefer to leave the user in the application and just hit them with a flashmessenger notice and leave them on the page to make another request (if appropriate):
public function indexAction() {
//get form and pass to view
$form = new Form();
$this->view->form = $form;
try {
//get form values from request object
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost())) {
$data = $form->getValues();
// Do some stuff
}
}
} catch (Zend_Exception $e) {
$this->_helper->flashMessenger->addMessage($e->getMessage());//add message to flashmessenger
$this->_redirect($this->getRequest()->getRequestUri());//perform redirect to originating page so the messenger will flash
}
}
This is simple and works well when the possibility for incorrect user input exists.

unable to to open redirected page after facebook authentication

i am using facebook connect under codeigniter.after authentication i want to redirect on success method of my controller
here is my controller:
class Welcome extends CI_Controller {
function __construct()
{
parent::__construct();
$this->load->model('Facebook_model');
}
function index()
{
$fb_data = $this->session->userdata('fb_data');
$data = array(
'fb_data' => $fb_data,
);
$this->load->view('welcome', $data);
}
function topsecret()
{
$fb_data = $this->session->userdata('fb_data');
if((!$fb_data['uid']) or (!$fb_data['me']))
{
redirect('welcome');
}
else
{
$data = array(
'fb_data' => $fb_data,
);
$this->load->view('topsecret', $data);
}
}
function success()
{
$this->load->view('welcome_message');
}
}
my model for facebook api access:
class Facebook_model extends CI_Model {
public function __construct()
{
parent::__construct();
$config = array(
'appId' => '261066574000678',
'secret' => ' 79e11f65449988965362f58e9a4aabd7',
'fileUpload' => true, // Indicates if the CURL based # syntax for file uploads is enabled.
);
$this->load->library('Facebook', $config);
$user = $this->facebook->getUser();
// We may or may not have this data based on whether the user is logged in.
//
// If we have a $user id here, it means we know the user is logged into
// Facebook, but we don't know if the access token is valid. An access
// token is invalid if the user logged out of Facebook.
$profile = null;
if($user)
{
try {
// Proceed knowing you have a logged in user who's authenticated.
$profile = $this->facebook->api('/me?fields=id,name,link,email');
} catch (FacebookApiException $e) {
error_log($e);
$user = null;
}
}
$fb_data = array(
'me' => $profile,
'uid' => $user,
'loginUrl' => $this->facebook->getLoginUrl(
array(
'scope' => 'email,user_birthday,publish_stream', // app permissions
'redirect_uri' => 'https://sanjay.localhost.com/index.php/welcome/success' // URL where you want to redirect your users after a successful login
)
),
'logoutUrl' => $this->facebook->getLogoutUrl(),
);
$this->session->set_userdata('fb_data', $fb_data);
}
}
since i am testing this on localhost host,i also edited my host file and changed my localhost hostname to sanjay.localhost.com.redirect happens but not happens..i think may be because of querystring.when redirects happens redirect uri is
=">https://sanjay.localhost.com/index.php/welcome/success?state=ff5712299510defa&code=AQCaD-FAd1shuW#=
i am not understanding how to handle state and code inside of query string.
please help.
Thank you for contacting me on my blog. First of all, Facebook is discontinued the localhost support. Her is the link https://developers.facebook.com/bugs/128794873890320.
I have not developed any app using codeigniter, I use CakePHP but the auth follow should be same.
1. Create a fb_login function in user controller.
2. This function will follow this logic.
a. Use $facebook->getUser() to get user id.
b. Then use $facebook->api('/me') to be sure.
3.If you get FacebookApiException then send user to login with Facebook. If you use official SDK then the current url will be added to redirect url.
4.the Facebook will redirect your user after sign in. so you will get data using $facebook->getUser(). Save this data in session for further use in you app. then redirect user to you control page or any other page. CakePHP has setFlash() function wich show what ever msg set in the control panel in view. I think Codeignator should have some thing like this. If not you can simply set a msg in session and redirect user. Then unset the msg after showing the msg.
Here is full code
$uid = $facebook->getUser();
try {
$user_profile = $facebook->api('/me');
} catch (FacebookApiException $e) {
//echo $e->getMessage();
$uid = null;
}
$loginUrl = $facebook->getLoginUrl(
array(
'scope' => 'publish_stream,offline_access,email'
),''
);
if (!$uid) {
echo "<script type='text/javascript'>top.location.href = '$loginUrl';</script>";
exit;
}
//search using uid on your user table to check if the use is returnign user or new user.
if($new_user ==1)
{
$this->Session->setFlash('please sign up');
//redirect to sign up
}
else
{
$this->Session->setFlash('you are good lad');
//reditect to control panel
}

cakephp auth component allow redirect issue

I am having problem with Auth component when I use $this->Auth->allow('index','view');
I am getting /users/login has resulted in too many redirects when I use $this->Auth->allow('*') it works fine. I am using cakephp 1.3.12 here is app_controller.php
class AppController extends Controller {
var $components = array('Auth','Session');
function beforeFilter(){
$this->Auth->allow('index','view');
}
}
I changed the app_controller.php
class AppController extends Controller {
var $components = array('Auth','Session');
function beforeFilter(){
$this->Auth->allow(array('index','view','display'));
}
}
users_controller.php
class UsersController extends AppController {
var $name = 'Users';
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow(array('login','logout'));
}
function login() {
if ($this->Session->read('Auth.User')) {
$this->redirect('/', null, false);
}
}
routes.php
Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
any suggestions?
Thanks
Don't know but you might want to check if you have any request actions.
"If you are using requestAction in your layout or elements you should allow those actions in order to be able to open login page properly."
http://book.cakephp.org/1.3/en/view/1257/allow
This had me stumped for the longest time.
Let's say you render an element somewhere in your template:
echo $this->element('comments');
And in views/elements/comments.ctp you have something that requests an action like
$comments = $this->requestAction('comments/index');
foreach($comments as $comment) {
// print stuff
}
In your CommentsController your have to:
function beforeFilter() {
$this->Auth->allow('index');
}
Notice you are requesting an index action from your comments controller in your element. That's why you have to allow 'index' for that specific controller.
I haven't seen this problem properly addressed anywhere. Hope that's what is causing your error.
its an array =)
$this->Auth->allow(array('index','view'));
your getting the too many redirects message becasuse the /user/login action is not accessible. So the server tries to display the login page, but it can't, because regular non-connected users dont have acces to /user/login. And when a user doesn't have access to a page, the server will redirect him to the login page... so you see, its an infinite loop.
The /user/login action should be authorized to everyone. Your Users controller should look like this:
class UsersController extends AppController {
var $name = 'Users';
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow(array('login','logout'));
}
function login(){
if ($this->Session->read('Auth.User')) {
$this->redirect('/', null, false);
}
}
//if you're using prefix routes.
function admin_login(){
$this->redirect('/users/login');
}
if this doesn't the problem, maybe you're redirecting the page in the routes.php
Hope this helps
you are doing it wrong.How can app can get to know that which of your controller action you are trying to controller.Do it from your controller.
remove this from app
$this->Auth->allow(array('index','view','display'));
try this in your app controller with needed change
$this->Auth->loginError = "Wrong credentials. Please provide a valid username and password.";
$this->Auth->authError = "You don't have sufficient privilege to access this resource.";
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'dashboard');
do this from your user controller
$this->Auth->userModel = 'User';
$this->Auth->allow('*');
And in your login dont do anything all of your redirect and all will be doing by app controller.
If you have any doubt regarding this mail me
jafarkv9#gmail.com

How to handle Facebook's signed_request for iFrame Canvas applications?

I'm developing an iFrame Canvas application for Facebook using CakePHP, its Auth component, WebTechNick's Facebook plugin and OAuth for canvas pages (I've enabled this in the Facebook Developer app options). I would like users to be able to use the application after adding it to their profile (by requesting email and publish_stream permissions) by visiting http://apps.facebook.com/myapp/ or as a tab in their profile.
Requesting permissions is not the problem. The user is redirected to the permissions request page and then redirected to a callback method which requests an access_token, as per this tutorial.
After this callback the user is redirected back to http://apps.facebook.com/myapp/ which shows their personal index page. This is also where the problems start. As soon as the aforementioned URI is loaded, the browser asks for a form resubmission, this happens every time I reload http://apps.facebook.com/myapp/. This is the case because Facebook wants to pass the (expected) signed_request parameter and I'm wondering what to do with it. It's not an empty variable, so do I need another validation method or redirect, perhaps?
How should I handle the procedure for the signed_request parameter and, more importantly how to get rid of this form resubmission dialog?
Some of my methods, they might be a bit of a mess due to all the experimentation of the past day.
beforeFilter, login and callback methods, in my UserController.php:
function beforeFilter() {
parent::beforeFilter();
if (empty($this->permissions)) {
$this->Auth->allow('login', 'logout', 'callback');
}
}
function login() {
$session = $this->facebook->getSession();
$login_url = 'https://graph.facebook.com/oauth/authorize?client_id=' . FACEBOOK_APP_ID . '&redirect_uri=' . MY_APP_URL . '/users/callback/&type=user_agent&&display=page&scope=' . FACEBOOK_APP_PERMISSIONS;
if($session){
try {
$uid = $facebook_client->getUser();
$me = $facebook_client->api('/me', $params);
print($me);
} catch (FacebookApiException $e) {
error_log($e);
}
} else {
$this->set('authorise', true);
$script = '$(document).ready(function() { facebookRequestPermissions("'.$login_url.'");});';
$this->set('script', $script);
}
}
function callback() {
function callFb($url, $params) {
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_POSTFIELDS => http_build_query($params),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_VERBOSE => true
));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
$params=array('client_id'=>FACEBOOK_APP_ID, 'type'=>'client_cred', 'client_secret'=>FACEBOOK_APP_SECRET);
$url = "https://graph.facebook.com/oauth/access_token";
$access_token = callFb($url, $params);
$access_token = substr($access_token, strpos($access_token, "=")+1, strlen($access_token));
if ($access_token) {
$this->redirect(FACEBOOK_APP_URL);
} else {
echo 'An error has occurred';
}
}
The JavaScript in the login method refers to this jQuery function, the Facebook JavaScript SDK is initialised in $(document).ready():
function facebookRequestPermissions(login_url) {
FB.getLoginStatus(function(response) {
if (response.status !== 'unknown') {
top.location.href=login_url;
}
});
}
The JavaScript function should only fire when a user is logged in, if not, a different landing page is shown.
I use some methods in an overall AppControler:
class AppController extends Controller {
var $components = array('RequestHandler', 'Session', 'Auth', 'Facebook.Connect');
var $helpers = array('Form', 'Time','Html','Javascript', 'Session', 'Facebook.Facebook');
protected $facebook;
protected $permissions;
private $user;
function beforeRender() {
//Save the username if it isn't already present
if ((int)$this->Auth->user('id') != '' && (string)$this->Auth->user('username') == '') {
$data = array('id' => (int)$this->Auth->user('id'), 'username' => (string)$this->user['username']);
$this->loadModel('User');
$this->User->save($data);
}
if (!empty($this->user) && !empty($this->permissions)) {
$this->set('currentUser', $this->Auth->user());
}
}
function beforeFilter() {
$this->Auth->autoRedirect = false;
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'index');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
App::import('Lib', 'Facebook.FB');
$this->facebook = new FB();
$this->user = $this->facebook->api('/me');
$this->permissions = $this->facebook->api('/me/permissions');
}
}
EDIT:
This only seems an issue with Firefox. Chrome doesn't display the dialog, but instead does a silent refresh after which the signed_request parameter is empty, strangely enough. This isn't the case with Firefox, where the signed_request parameter remains the same after every prompted refresh (unless the iFrame content is cached), which is looping infinitely, it seems.
EDIT 2:
Still struggling with this, but I ended up disabling the OAuth 2.0 for Canvas option in the Facebook Developer application, which has resolved the form resubmission issue. Of course this is not a real solution, because OAuth 2.0 is becoming mandatory for canvas application on Facebook, I believe.
Since I can't test the whole thing I am not sure if this is right, but on the first sight your JavaScript function looks strange to me. It looks like you always redirect to the login url, although the user gave permission.
Refering to Facebook JavaScript SDK, the function should look like this:
function facebookRequestPermissions(login_url) {
FB.getLoginStatus(function(response) {
if (!response.session) {
top.location.href=login_url;
}
});
}
or, if you want to call .status:
if (response.status == 'unknown')
About your question concerning the signed_request: it is used to get some information, look at Authentication - Signed Request to see what exactly. You don't need another validation method.