CakePHP doesn't set any cookies and session after changing server - php

I have an application based on CakePHP version 3.2.10. I'm totally new in CakePHP so sorry if it is a banal problem. In my application I use CSRF component and Auth component configured in this way:
$this->loadComponent('Auth', [
'authorize'=> 'Controller',
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
],
'scope' => [
'Users.active' => 1,
]
]
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'logoutAction' => [
'controller' => 'Users',
'action' => 'logout'
],
'logoutRedirect' => [
'controller' => 'Pages',
'action' => 'index'
],
'unauthorizedRedirect' => '/', // $this->referer()
]);
and login action like
public function login()
{
$this->set('title', 'Logowanie');
$this->set('bodyclass', 'main-page');
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
if($user['blocked'] == 0) {
$this->Auth->setUser($user);
if ($this->Auth->user('role') == 'admin')
return $this->redirect(['controller' => 'Admin', 'action' => 'index']);
return $this->redirect($this->Auth->redirectUrl());
}
else{
$this->Flash->error('Konto zostało zablokowane przez administratora serwisu. Skontaktuj się z Biurem Obsługi.');
}
} else $this->Flash->error('Błędne dane logowania. Spróbuj ponownie.');
}
}
Now the problem:
Few days ago I changed server where application is running, and after changing it logging in stopped working. After clicking login there is a message CSRF Token cookie is missing. To test if the component is the problem i disabled csrf and try again then white screen appears and nothing happen if i refresh page i'm not logged in. I checked the working version and not working version and realized that Cake not store any cookies on new server, while on old there is everything ok and cookies are set.
After few researches i found out that not only cookies not work but all sessions. I try to dump $_SEESION but it shows something only right after calling $this->Auth->setUser($user), and nowhere else. So i look through some solutions and find that there is a setting in config/app.php to set up the session:
'Session' => [
'defaults' => 'php',
],
And read that when set in that way the session is stored in default php dir. So i changed it to cake(even make a dir sessions in tmp folder and added 777 permissions). But the problem was not solved. I have no idea why it not work. I tried setting cookiePath and other settings i Session config, but it still not work.
I think that this may be the server problem(permissions). So few words about servers: Old server where everything was working was my private server(i have full access), new server(or maybe virtual server/host) is on one of hosting companies(home.pl) where i have almost no privileges to configure.

Make sure you follow these steps:
//For Set
var $var = array('Cookie');
//For Write
$this->Cookie->write('YOUR DESIRED NAME', cookieData, $expires = null);
//For Read
$this->Cookie->read('YOUR DESIRED NAME');

Check in your Conroller code should be in src/Controller/AppController.php for below points
1) have you loaded cookie component properly in initialize() or beforeFilter() method?
2) do you have configured domain and path in your cookie configuration using $this->Cookie->configKey(), if Yes, then change domain to new domain or remove domain configuration.
3) delete cache folders for model and persistence from tmp folder
For more information about Cookie refer Document

Related

cakePHP 4 how to change session.cookie_lifetime

I have time in seconds when the session should end. If the user has not selected the checkbox "remember_me" - the session will last 2 hours. When the checkbox is selected - should last - 48 hours. I have a loginСontroller, where I react - to the login result and if the validation is successful and checkbox = "on" you need to change the session time. I tried to change by looking at the documentation, and spent a lot of time looking for a solution. Maybe someone can help me. Thank you very much in advance[enter image description here]
here is my file config/app.php
'Session' => [
'defaults' => 'php',
'ini' => [
'session.cookie_lifetime' => 7200,
]
],
and here is my loginController
`public function index()
{
$this->viewBuilder()->setLayout('main');
$this->set("title", "");
$this->set("description", "description");
$this->request->allowMethod(['get', 'post']);
$result = $this->Authentication->getResult();
// regardless of POST or GET, redirect if user is logged in
if ($result->isValid()) {
if ($this->request->getData('remember') == 'on') {
///// the solution should be here
}
$redirect = [
'controller' => 'Main',
'action' => 'index',
];
return $this->redirect($redirect);
}
// display error if user submitted and authentication failed
if ($this->request->is('post') && !$result->isValid()) {
$this->Flash->saved_error(__('Invalid email or password'));
}
}`
You most likely shouldn't do it that way, your controller code shouldn't have to know about such details if it can be avoided.
The authentication plugin ships with a cookie based authenticator that you can use in addition to the session authenticator, that way you can extend authentication beyond the default session lifetime, I'd suggest that you look into that instead.
$service->loadAuthenticator('Authentication.Cookie', [
'fields' => $fields,
'loginUrl' => $loginUrl,
'cookie' => [
// cookie expires in 2 days from now
'expires' => \Cake\Chronos\Chronos::now()->addDays(2)
],
]);
By default the authenticator looks up a field named remember_me, so either rename that in your template, like:
echo $this->Form->control('remember_me', ['type' => 'checkbox']);
or configure the authenticator's rememberMeField option with the custom field name that you're using in your form.
See also
Authentication Cookbook > Authenticators > Cookie Authenticator

Why is a login via `auth guard A` redirected different than one from `auth guard B` when the same behaviour is expected?

First of all, i'm not a pro in PHP development or Laravel but will try to explain my question as well as possible for me. I also try to give enough information but when something is missing, please let me know whats missing and i will add it!
Some important context about my project:
Laravel 6.18 (will update soon if my first goal is reached)
I use Hyn/Multi-tenant to make my application mutli tenant.
I use a Vue front end, i give a bearer token to Vue via the app.js
The application should be a multi tenant application where each tenant has its own user table. I first built everything as "single tenant". When the basic functionality was implemented and worked fine, i added Hyn/MT to make it multi tenant, moved the tenant specific tables to the tenant db folder and updated the models. Now i know it was better to start with Hyn/MT before building all the functionality but so far i got everything working fine.
After i added the multi tenant support to my project and fixed the broken functionality i decided to add an admin specific area to manage the tenants. To achieve this i added a SystemU ser table to the master database which contains the admin users. After that i update my web.php so it gives the subdomain to the LoginController.guard() function. My code:
// web.php
Route::group(array('domain' => '{subdomain}.festipay.xlan'), function () {
Route::post('login', 'Auth\LoginController#login');
});
// LoginController.php
protected function guard()
{
if (Request::route("subdomain") == "admin") {
return Auth::guard('admin_web');
} else {
return Auth::guard('web');
}
}
I also updated my config/auth.php, it looks like this now:
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin_web' => [
'driver' => 'session',
'provider' => 'admin_users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin_users' => [
'driver' => 'eloquent',
'model' => App\SystemUser::class,
]
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
Except for the mentoined changes i did not implement any admin specific logic yet. So i expect that the admin users are handeled exactly the same except for the way they are authenticated.
The tenant users who log in to e.g. tenant_x.domain.com are redirected to /dashboard when they login and are redirected back to /login when they log out. The admin users who log in to admin.domain.com are not redirected to /dashboard when the login is successfull but are redirected back to /login again. Offcourse this is not the expected behaviour as it should be (currenly) the same as the tenant users (so a redirect to /dasboard when the login is succesfull)
I think that the authentication them selve works fine as the LoginController.attemptLogin() returns true when i use valid admin credentials and false (and view shows wrong credetials) when i use invalid credentials.
I found in this post that is may be a session issue and i tried to apply the solution mentoined in that post. Unfortunately did adding protected $primaryKey = 'id'; to the SystemUser class not solve the issue. I also compared the tenant User class with the SystemUser class but they are almost identical exccept for unimportant fields i removed from the SystemUser like address.
I have no idea how i can find out where the issue occurs or how to solve this. The goal is that an admin which logged in succesfully is redirect to another page as the /dashboard. Can someone help me find out what goes wrong? i'm already happy when someone can help me to get the same behaviour for the admin's as the tenants currently have.
Thanks in advance!
Update 1 #David Barker
When its about the session, i think this is important to know as well:
- I use a Vue front end, i give a bearer tokento Vue via theapp.js``
My session config:
<?php
use Illuminate\Support\Str;
return [
'driver' => env('SESSION_DRIVER', 'file'),
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
'encrypt' => false,
'files' => storage_path('framework/sessions'),
'connection' => env('SESSION_CONNECTION', null),
'table' => 'sessions',
'store' => env('SESSION_STORE', null),
'lottery' => [2, 100],
'cookie' => env(
'SESSION_COOKIE',
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
),
'path' => '/',
'domain' => env('SESSION_DOMAIN', null),
'secure' => env('SESSION_SECURE_COOKIE', false),
'http_only' => true,
'same_site' => null,
];
I did a dd($request->session();) in the LoginController->attemptLogin(); function, see the result bellow. The result is the same for both users except for the id and _token. (i cleaned the cookies before the login attempt in both cases)
Illuminate\Session\Store {#490 ▼
#id: "7WI7JUWPnS4pg3EHvaxk5TOKaM9l9UXJi1zJNKuG"
#name: "festipay_session"
#attributes: array:1 [▼
"_token" => "mtMWanYGMUxFHivOqAaEmVQnHDE0hvwKkHMgCswg"
]
#handler: Illuminate\Session\FileSessionHandler {#489 ▼
#files: Illuminate\Filesystem\Filesystem {#198}
#path: "/var/www/domain.com/storage/framework/sessions"
#minutes: "120"
}
#started: true
}
Maybe this is also interesting infomation. Left are the responses for the admin (after i clicked the login button) and right the working tenant login.
I finally found the issue. It was very easy to solve. I did not specify the auth guard for the Route::group.
It was like this:
Route::group(['middleware' => 'auth'], function () {
Route::get('/', function () { return redirect('/dashboard'); });
Route::get('/dashboard', function () { return view('dashboard'); })->name('dashboard');
Route::get('/logout', 'Auth\LoginController#logout')->name
I changed it to this to make it work:
Route::group(['middleware' => 'auth:system_user_web,web'], function () {
Route::get('/', function () { return redirect('/dashboard'); });
Route::get('/dashboard', function () { return view('dashboard'); })->name('dashboard');
Route::get('/logout', 'Auth\LoginController#logout')->name

Need to clear Google OAuth cache?

I set up the Google OAuth with Hybridauth and it works from my development folder e.g. http://example.com/dev and callback is located at http://example.com/dev/callback.php
Now I want to move the dev folder to the production folder e.g. http:/example.com/backend and callback is changed to http://example.com/backend/callback.php
I changed the Authorized redirect URIs from Google console, but Google keeps the old callback link when I do log in.
Is it necessary to clear cache from Google or wait for an amount of time to let Google update the changes?
$config = [ 'callback' => 'example.com/backedn/callback.php',
'providers' => [ 'Google' => [ 'enabled' => true,
'keys' => [ 'id' => 'xxxxx.apps.googleusercontent.com',
'secret' => 'xxxxxxx', ],
'scope' => 'email', ], ], ];
Sorry for my careless mistake as I call the old script from the popup
function auth_popup( provider ){
// replace 'path/to/hybridauth' with the real path to this script
var authWindow = window.open('http://example.com//dev/callback.php?provider='+provider, 'authWindow', 'width=600,height=400,scrollbars=yes');
return false;
}

How to get Response from Facebook PHP SDK after Succesful login

I am using CakePHP 2.4 and Facebook PHP SDK 3.2.3 to create login form, which has two options: local login and login with facebook. I have been referring to How do I integrate Facebook SDK login with cakephp 2.x? on how to create login actions and view for Facebook login.
Now I have working local login, and when I press to facebook login, I am redirected to Facebook and back again to my login action of plugin Users. However I get no response in $_GET['code'] or $this->request->query['code'].
This is my login action:
public function login() {
if ($this->Auth->user()) {
$this->Session->setFlash(__('You are already registered and logged in!'), 'flash_info');
$this->redirect('/');
}
if ($this->request->is('post')) {
if ($_SERVER['HTTP_HOST'] == Configure::read('webakis.touchscreen_host')) {
$this->request->data['User']['username'] = $this->request->data['User']['name'] . ' ' . $this->request->data['User']['surname'];
}
if ($this->Auth->login()) {
$this->User->id = $this->Auth->user('id');
$this->User->saveField('last_login', date('Y-m-d H:i:s'));
if ($this->here == $this->Auth->loginRedirect) {
$this->Auth->loginRedirect = '/';
}
$this->Session->setFlash(sprintf(__('%s you have successfully logged in'), $this->Auth->user('name')), 'flash_success');
if (!empty($this->request->data)) {
$data = $this->request->data[$this->modelClass];
$this->_setCookie(array('name' => 'AKIS'), 'User');
}
if (empty($data['return_to'])) {
$data['return_to'] = null;
}
$this->redirect($this->Auth->redirect($data['return_to']));
} else {
$this->Session->setFlash(__('Invalid e-mail / password combination. Please try again'), 'flash_error');
}
}
// When facebook login is used, facebook always returns $_GET['code'].
if($this->request->query('code')){ //Execution doesn't get in here
Debugger::log($this->request->query);
// User login successful
$fb_user = $this->Facebook->getUser(); # Returns facebook user_id
if ($fb_user){
Debugger::log($fb_user);
$fb_user = $this->Facebook->api('/me'); # Returns user information
// We will varify if a local user exists first
$local_user = $this->User->find('first', array(
'conditions' => array('email' => $fb_user['email'])
));
// If exists, we will log them in
if ($local_user){
$this->Auth->login($local_user['User']); # Manual Login
$this->redirect($this->Auth->redirectUrl());
}
// Otherwise we ll add a new user (Registration)
else {
$data['User'] = array(
'username' => $fb_user['email'], # Normally Unique
'password' => AuthComponent::password(uniqid(md5(mt_rand()))), # Set random password
'role' => 'registered'
);
// You should change this part to include data validation
$this->User->save($data, array('validate' => false));
$this->redirect('/');
// After registration we will redirect them back here so they will be logged in
//$this->redirect(Router::url('/users/login?code=true', true));
}
}
}
if (isset($this->request->params['named']['return_to'])) {
$this->set('return_to', urldecode($this->request->params['named']['return_to']));
} else {
$this->set('return_to', false);
}
$allowRegistration = Configure::read('Users.allowRegistration');
$this->set('allowRegistration', (is_null($allowRegistration) ? true : $allowRegistration));
Configure::write('keyboard', 1);
}
Without getting response here, I can't handle my facebook login logic.
These are responses I get then facebook redirects me back to login page:
Two interesting requests (with Url params and location shown):
GET oauth?....
client_id=671394586273323
ext=1406709978
hash=AeYXzQgAjCbRdY4g
redirect_uri=http://127.0.0.1.xip.io/users/users/login
ret=login
sdk=php-sdk-3.2.3
state=9d030270aa50f2e52ac4aa66a37cd0fd
Location: http://127.0.0.1.xip.io/users/users/login?code=AQC_UtjKJU8a2leJMFAB4_1qx1mh1ww0-sRWdAD5vCocfKuZPTF4iSdKYwqxQUsm9N-_1tSPGfh3LYQXbXjYeY2onVBD6gTvJ5amvRZm5ZjI1OSYoLkqgjBsdfjWSdXTigCIQLf5d180keXTCf5jRiOXi8pWi0V2UxXqVl4K9QWWq2qGfSGuXlJMr32NZqKYR0Z93LyR1EiRFJPohfo6-j0kZJrNTkljCbY16Nrq1InqQLdYwGCOSg4IrbR0auaMIWTlnUCKFCr4DT3If_5HPFEDM6ZigeUvURM-q8y-CxDrRIctSmT4Bz1UevPqR-hOMbgKGzYUplatRywzjq_-R7bt&state=9d030270aa50f2e52ac4aa66a37cd0fd#_=_
GET login?code....
code=AQC_UtjKJU8a2leJMFAB4_1qx1mh1ww0-sRWdAD5vCocfKuZPTF4iSdKYwqxQUsm9N-_1tSPGfh3LYQXbXjYeY2onVBD6gTvJ5amvRZm5ZjI1OSYoLkqgjBsdfjWSdXTigCIQLf5d180keXTCf5jRiOXi8pWi0V2UxXqVl4K9QWWq2qGfSGuXlJMr32NZqKYR0Z93LyR1EiRFJPohfo6-j0kZJrNTkljCbY16Nrq1InqQLdYwGCOSg4IrbR0auaMIWTlnUCKFCr4DT3If_5HPFEDM6ZigeUvURM-q8y-CxDrRIctSmT4Bz1UevPqR-hOMbgKGzYUplatRywzjq_-R7bt
state=9d030270aa50f2e52ac4aa66a37cd0fd
Location: http://127.0.0.1.xip.io/eng/login
I find it strange with code is not URL parameter in first request, but it is in URL, second response shows inverse situation.
OK, so this might be routing issue.
$this->set('fb_login_url', $this->Facebook->getLoginUrl(array('redirect_uri' => Router::url(array('plugin'=> 'users', 'controller' => 'users', 'action' => 'login'), true))));
Router::url() returns: http://127.0.0.1.xip.io/users/users/login
My Router connects:
Router::connect('/:language/login', array('plugin' => 'users', 'controller' => 'users', 'action' => 'login'), array('language' => '[a-z]{3}'));
Router::connect('/:language/logout', array('plugin' => 'users', 'controller' => 'users', 'action' => 'logout'), array('language' => '[a-z]{3}'));
Router::connect('/:language/register', array('plugin' => 'users', 'controller' => 'users', 'action' => 'add'), array('language' => '[a-z]{3}'));
Router::connect('/:language/users', array('plugin' => 'users', 'controller' => 'users'), array('language' => '[a-z]{3}'));
Router::connect('/:language/users/index/*', array('plugin' => 'users', 'controller' => 'users'), array('language' => '[a-z]{3}'));
Router::connect('/:language/users/:action/*', array('plugin' => 'users', 'controller' => 'users'), array('language' => '[a-z]{3}'));
Router::connect('/:language/users/users/:action/*', array('plugin' => 'users', 'controller' => 'users'), array('language' => '[a-z]{3}'));
Al these urls redirects to: http://127.0.0.1.xip.io/eng/login . This might be problem, as Facebook SDK have different redirect URL, so then it redirects to http://127.0.0.1.xip.io/users/users/login , there is also request to redirect to http://127.0.0.1.xip.io/eng/login. The query parameter 'code' may get lost after this redirection.
As can be seen in the network console and the from the headers that you've posted, your server is doing an additional redirect, and as already mentioned in my comment this is where the query parameters are being lost.
It looks like the redirect URL that you are passing to Facebook is wrong, it is
http://127.0.0.1.xip.io/users/users/login
which contains most likely at least a surplus users (not sure where this stems from, you'll have to figure that on your own) unless you are using a plugin called Users which contains a controller named UsersController, so maybe it should be more like
http://127.0.0.1.xip.io/users/login
However, considering that your server redirects to /eng/login when accessing /users/users/login, it may be that the redirect URL passed to Facebook is wrong altogether.
So first you'll have to figure out the actual proper URL of your login, and then make sure that Router::url() (assuming you are using the code from the linked question) actually generates that exact URL.
Update
From looking at your updated question, I don't see why any of these routes should cause a redirect, they aren't redirect routes, but just routes. The actual redirect will stem from somewhere else.
In case the redirect to /eng/login is acutally indended, then you'll have to make sure that you pass that URL as the redirect URL to Facebook, that is properly invoke Router::url() with the language key set so that it will match your first route
Router::url(
array(
'plugin' => 'users',
'controller' => 'users',
'action' => 'login',
'language' => 'eng'
),
true
);
or use the persist option in your routes so that a call to Router::url() without the language key will match your route in case the current URL contains the language element, something along the lines of
Router::connect(
'/:language/login',
array('plugin' => 'users', 'controller' => 'users', 'action' => 'login'),
array('language' => '[a-z]{3}', 'persist' => array('language'))
);
// ...
// connect all default routes with a persistent language prefix
Router::connect(
'/:language/:controller/',
array('action' => 'index'),
array('language' => '[a-z]{3}', 'persist' => array('language'))
);
Router::connect(
'/:language/:controller/:action/*',
array(),
array('language' => '[a-z]{3}', 'persist' => array('language'))
);
or, as a last option, include the query string in the redirect to /eng/login
array(/* other route params */, '?' => $this->request->query)
Due to Router issues I changed redirect uri parameter in $this->Facebook->getLoginUrl() to 'redirect_uri' => Router::url('/', true).Configure::read('Config.language').'/login' , as it resolves to http://127.0.0.1.xip.io/eng/login .
Now I get $this->request->query('code') param, so my code should work for now.

Does Cakephp Auth can be use even in other controller?

Recently, I've been studying cake, I've seen the auth library which said to be will take care of the access control over your app, but, it seems like, you can't initialize or even use this auth library when you're not in the 'UsersController', i did not want that, what if it has some admin part wherein i want the URI to be admin/login, or just simply /login, i've been scratching my head over this one, please help.
Another question, why it seems like the functionality of the '$this->redirect' is not effective when i'm putting this one at any method that contains nothing but redirection, or even in the __construct()?
thanks guys, hoping someone could clearly explain to me those things.
you can use the Auth component inside any controller in the application. If you want it will only effect with the admin section then you can add condition in the beforeFilter funciton in you application AppController on Auth initialization like.
// for component initialization.
public $components = array(
'Auth' => array(
'authenticate' => array(
'userModel' => 'Customer', // you can also specify the differnt model instead of user
'Form' => array(
'fields' => array('username' => 'email')
)
)
)
}
and you can bind this on the admin routing like
function beforeFilter(){
// only works with admin routing.
if(isset($this->request->params['prefix']) && ($this->request->params['prefix'] == 'admin')){
$this->Auth->loginRedirect = array('admin' => true, 'controller' => 'pages', 'action' => 'index');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login', 'admin' => true);
$this->Auth->loginAction = array('admin' => true, 'controller' => 'customers', 'action' => 'login');
}
}
If you're using cake 2.3.x or later then make sure you have specified the redirect action in correct format like.
return $this->redirect('action_name'); // you can also specify the array of parameters.

Categories