I have a Laravel site that redirects to a payment provider (external third party website). When the user completes their payment, they are redirected back to my site via a POST request.
The issue I'm having is that the user's session is lost when they return to the confirmation page.
I wondered if this was behaviour of PHP generally but it seems to be specific to Laravel.
I have checked my sessions.php config file and can confirm the following is set 'expire_on_close' => false,.
I've created a very basic example of the issue below
My website (pre-sale)
Controller
public function redirect()
{
$user = Auth::user();
dd($user); // returns User model;
redirect()->away('http://www.example.com');
}
Payment provider website
Note, the request is sent via the application within the browser - not a callback. There is also no button. I just want to demonstrate the POST back to the Laravel site.
<html>
<head></head>
<body>
<form method="POST" action="http://www.example.com/payment/confirmation">
<input type="submit">
</form>
</body>
</html>
My website (post-sale)
Route
Route::post('/payment/confirmation', 'Payment\PaymentController#confirmation');
Controller
public function confirmation()
{
$user = Auth::user();
dd($user); // Returns null
}
I have added the path to the VerifyCsrfToken middleware's exception array. Is there anything within Laravel that would destroy the session on POSTing via an external website? I'm sure I'm missing something obvious. Thanks
I was able to resolve this issue by changing 'same_site' => 'lax', to 'same_site' => null, in config/session.php. This appears to be a new setting in Laravel 7+.
I'm not sure if there are any security implications caused by this change without further reading but this, for now, fixes the problem. It would be a nice feature to somehow whitelist certain domains.
Setting the 'same_site' => null might not work as expected again because Standards related to the Cookie SameSite attribute recently changed https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
The new default is now Lax, so if you set your same_site to null, the browser will assume the default of Lax which is not the policy you need.
You need to explicitly set 'same_site' => "none", a value of none for same_site requires your connection to be secure, hence, set 'secure' => true.
'same_site' => "none"
'secure' => true
In my testing, it seems the session is not actually destroyed. However, it is not loaded when receiving the external POST request, and the session will be destroyed if you allow the return to the main site to trigger a new session save. By sending a header() redirect and terminating the process before a new session can be saved, it seems that it is possible to restore the existing session.
Okay, perhaps it's a bit gross?
Route::match(['get','post'],'/payment/confirmation','Payment\PaymentController#confirmation');
public function confirmation(Request $request)
{
// assert cookie or reload current URL
if (! $request->hasHeader('Cookie')) {
header('Location: '.url()->current());
exit;
}
$user = Auth::user();
dd($user); // user exists!
}
I'm not going to say this is a great solution. It does feel a bit hackish. Some additional testing may be required. But at first blush, it does seem to work — at least on my end. And maybe it gives some additional insight into what's actually going on behind the scenes.
Also, I'm not really sure whether you want to expose the payment confirmation page to a GET request.
But this was an interesting rabbit hole to go down for a bit.
You should keep your cookie session to 'lax', you don't want your cookies available on other sites, etc.
The fix as I have found is to pass true for remember when you do a login.
Example:
Auth::login(
user: $user,
remember: true
);
$request->session()->regenerate();
Your user will now be logged in indefinitely until they log out, ie going away from your site and returning via redirect headers and all that. I discovered this when using Passport to receive a callback for Oauth and I needed to log the user in at that point but redirect them to a subdomain for their tenant.
Normally you'll just want the session to be available across your root domain and subdomains like .mydomain.com in your config/env.
Related
I just upgrade from Magento 1.9.3.3 (form key disabled) to 1.9.3.7. Now I cannot login to the backend. The error message is 'You did not sign in correctly or your account is temporarily disabled.'
I tried to reset the cookie domain, clear the cache and session from both server and browser and still no luck.
There are many posts concerning magento 2, but far less talking about magento 1. Does someone with magento 1 had and solved this issue? Thanks for your help in advance!
Check your admin_role DB table data against a backup. The data in mine got erased due to a DB server crash.
This was the only way to resolve this issue for me.
Magento admin panel login message shows which are set in admin session. Magento 1.9.X Mage::throwException() not works for login message.
So if you want to check the exact error(Event if wrong username & password) change in file app/code/core/Mage/Admin/Model/Session.php
public function login($username, $password, $request = null)
{
.....
} catch (Mage_Core_Exception $e) {
$e->setMessage(
Mage::helper('adminhtml')->__('You did not sign in correctly or your account is temporarily disabled.')
);
.....
}
.....
}
Every times show below message so instead of that set error message as below.
public function login($username, $password, $request = null)
{
.....
} catch (Mage_Core_Exception $e) {
$e->setMessage($e->getMessage());
.....
}
.....
}
#himansu answer led me to discover that the ReCaptcha extension I had installed and enabled for the admin login was causing the error. Disabling the extension from MySQL cleared up the problem and allowed me to login.
In my case, I went to the core_config_data table and set 'aminvisiblecaptcha/backend/enabled' to a value of '0'. This is a setting for Amasty's Google ReCaptcha extension.
It occurrences when your username is blocked by many times of tries with wrong pass or other issues.
For me, I had a incredible cache, and I removed var/cache and var/full_page_cache, and tried to restart web server (nginx in my case).
So I had to debug, I found this class with authenticate method adminAuthenticate():
app/code/core/Enterprise/Pci/Model/Observer.php
PHP was bringing always wrong value with "first_failure" and "lock_expires" for correct register (my user).
So I just comment those lines, for clear the "trash":
// check whether user is locked
if ($lockExpires = $user->getLockExpires()) {
$lockExpires = new Zend_Date($lockExpires, Varien_Date::DATETIME_INTERNAL_FORMAT);
$lockExpires = $lockExpires->toValue();
if ($lockExpires > time()) {
throw new Mage_Core_Exception(
Mage::helper('enterprise_pci')->__('This account is locked.'),
self::ADMIN_USER_LOCKED
);
}
}
And post again login, so that's OK, and uncoment that lines. The problem did not occur anymore.
After a bit of googling around I found there are many people having various versions of issues which all have the common root to 'session keys'.
Solution:
go to 'System->configuration', in section (left menu) choose 'General->Web'. On the main content, go to 'Session cookie management'. All these parameters can be tweaked according to your needs. However, to solve the login problem, just change the cookie domain to '.example.com', replace the example with your domain name and the dot in the front of the string is important.
Explanation:
During login, magento will lay 2 cookies (among other sessions). one is 'domain.com'=>value1, the other one is '.domain.com'=>value2.
The value1 should equal to value2. If they don't, the cannot login will happen. By setting the '.example.com' as the domain instead of 'example.com', Magento will somehow force set these 2 values equally.
I have problem with only Chrome, all the other browsers will give equal answer to the values. Could this be a Chrome thing?
I've built a site in CI, and have a login system, which works fine in Firefox but not Chrome or IE. In those two if the username and password are correct it just redirects (i think) back to the login page, not to the login error page, or to the site home (as it should). I've noticed that it doesn't seem to be setting a cookie in Chrome, but it does in FF.
Here's the code in my controller which sets the cookie and redirects after authentication:
$newdata = array(
'username' => $_POST['login_username'],
'real_name' => $name,
'user_id' => $uid,
'logged_in' => TRUE
);
$this->session->set_userdata($newdata);
//echo $newdata;
redirect('/site/index');
Any ideas why this might be happening?
Thanks
I experienced this problem too ... session userdata lost for Chrome and IE, but ok on Firefox. It was due to an incorrect setting in config/config.php
I had to explicitly set cookie_domain
To make it automagic in the future I used this command ... ripped off the CI forums.
$config['cookie_domain'] = str_replace("http:/","",str_replace("https://","", $config['base_url']));
I added this line and all was ok.
Check that $_SESSION really is set. On CI you may need to exit gracefully to flush out whats in $this-sessionto the 'real' session variable.
add a echo serialize($_SESSION) so you know whats going on (before the redir).
and check how to end a Ci-request gracefully.
Adjusting the cookie name can fix the issue in the CI configs to remove underscores works wonders. The article CodeIgniter Session Problem in IE explains the details quite well.
For me it solved just adding more time to the cookie, I had 3600 so I changed to 7200. Seems to be a problem with time on my production site. May help someone.
In my case, the problem was that $config['base_url'] was empty.
So i set it to my domain
$config['base_url'] = 'http://yourdomain.com/';
CodeIgniter has some problem if the specified domain for cookies is localhost, I've set a fake domain with a real domain's name structure into the hosts file and it works.
I have used Yii::app()->user->returnUrl but it always redirect me to localhost/index.php. Is there any particular configuration or some pieces of code that other programs that I must write? If you have another solutions let me know it.
#aslingga, can you please explain what you're trying to do with returnUrl? Are you just trying to get back to where you were after logging in, or are you using it somewhere else?
This is from the Yii documentation:
Redirects the user browser to the
login page. Before the redirection,
the current URL (if it's not an AJAX
url) will be kept in returnUrl so that
the user browser may be redirected
back to the current page after
successful login. Make sure you set
loginUrl so that the user browser can
be redirected to the specified login
URL after calling this method. After
calling this method, the current
request processing will be terminated.
In other words, if the page you're trying to request requires authentication, the URI of the page you're on gets stored in a session variable. Then, once you've logged in, it takes you back to that page.
One way I'd recommend troubleshooting is to do a print_r($_SESSION); just to make sure the returnUrl is actually being stored. Then you'll be able to check if index.php is being stored as returnUrl or if you're just being redirected there for some reason.
Looking at the CWebUser methods getState and setState might also be helpful.
I know this question is old but maybe this will help someone out since I didn't couldn't find a decent answer anywhere.
How getReturnUrl works
Setting a default return URL for your Yii app requires a bit of customization. The way it works out of the box is that you specify the default return URL each time you call it:
Yii::app()->user->getReturnUrl('site/internal');
The idea being that if a user were to visit a page that requires authentication, they will get redirected to the login page, but not before the site running
Yii::app()->user->setReturnUrl('site/visitedpage');
Now when the user logs in, they will be returned to the page they intended to go to.
While I like that functionality, having to set the default return URL each time is dumb. If you want to change the default return URL, you have to go find it throughout your code. I suppose you could set the value in a site parameter and call
Yii::app()->user->getReturnUrl(Yii::app()->params['defaultReturnUrl']);
I don't think I have to explain why that solution is annoying too.
My Solution
So when getReturnUrl is called without any parameters, it returns either '/index.php' or just '/'. This is fine in some cases, but not always. This is better IMO.
First, extend the CWebUser class and add the following extras
class WebUser extends CWebUser {
// default return URL property
public defaultReturnUrl;
// override the getReturnUrl method
public function getReturnUrl($defaultUrl=NULL) {
if ($defaultUrl === NULL) {
$defaultReturnUrl = $this->defaultReturnUrl;
}
else {
$defaultReturnUrl = CHtml::normalizeUrl($defaultUrl);
}
return $this->getState('__returnUrl',$defaultReturnUrl);
}
}
Now, let's add a couple items to the user component array.
'user' => array(
'class' => 'WebUser',
'defaultReturnUrl' => 'site/internal'
)
Not only does this allow you to set a default return URL in the config, but also maintains the ability to set a different default return URL and use the setReturnUrl functionality.
I think, you must set it:
Yii::app()->user->setReturnUrl('controller/action');
I have a method in users_controller.php of my CakePHP project which is used to remotely log a user in through an AJAX call on a WordPress site. The method works flawlessly when called through Firefox, but when I attempt to call it either via AJAX or directly from the browser in IE8 or Safari, it simply will not log in. The Auth->login() method returns true as if everything is fine, but it does not log in. Any ideas?
function remoteLogin($key)
{
# this method should only be called via AJAX
$this->layout = 'ajax';
$matching_key = '***';
if($key == $matching_key)
{
# auto-login service account
$data['User']['username'] = '***';
$data['User']['password'] = $this->Auth->password('***');
$this->Auth->login($data);
}
}
Note: I have now confirmed that this method does not work in Opera either. I'm legitimately confused.
You might want to check your cookies and make sure they are being passed as you expect. Fiddler is helpful to see the http traffic as it goes by to figure out these AJAX issues.
Are www.domain.com and domain.com going to the same place?
If so this may be related to a CakePHP / IE issue I ran accross.
Delete any domain level cookies and see if it works.
In IE any domain cookies will take precidence over the subdomain cookies. So if you ever get a cookie going to domain.com and then later go to www.domain.com you can reset your session login, logout all day long but IE will ignore the www.domain.com cookies and continue to use the original domain.com one. I wrote a patch for an old version of Cake that would let you set/force the cookie scope to domain.com even when they are accessing the site as www.domain.com to get around this.
Don't now about IE8, but Safari does block cross-domain ajax, even between "siblings" under the same top domain. E.G. You can't have app.example.com load a div using ajax from helppages.example.com. Forget cookies, I am talking just plain html loaded using ajax.
I think the problem is your domain.
Ex: IE or some browser don't work if your domain like: abc_def.com, ...
Please check your domain and change it like abcdef.com => it'll be ok
Just want to know if anyone have the same problem.
The website need to login to perform certain task. We use stock Auth component to do the job.
Everything is fine until it hits an interface which build in Flash. Talking to Amf seems fine. But when the Flash player try to talk to other controller - got redirect because the session in not presented.
So basically when a user login - I need to somehow find a way to login the Flash player in as well.
ADDITION:
This only solve half of the problem.
Backtrack a little bit. How the Auth components evaluate the requester?
If the Session.checkAgent is true. They check if its the last one. So Flash has no chance they have a different Agent string.
OK now - Auth check them out - what? The Session cookie they store earlier ... so fail again.
UPDATE
Thanks for all the answers.
I have tried the suggested solution. Only one problem.
I am using Amf (as Cakephp Plugins) when I tried to test if the $this->params['actions'] is start with amf - it works sometime doesn't work sometime. Looking at "Charles" I can see they all call to the amf controller. Very puzzling ....
in config/core.php
try
Configure::write('Session.checkAgent', false);
It appears that if you manage to call your Session->id($sessionId) before any call to Session->read(), Session->check() or Session->write(), you don't need to bother with all the destroy old session, update userAgent and delete cookie stuff.
use this in beforeFilter action of your controllere called by flash:
if ($this->action == 'flashCalledAction') {
Configure::write('Security.level', 'medium');
//Using instead the session specified
$this->Session->destroy();
$this->Session->id($_REQUEST['sessionId']);
$this->Session->start();
// We revert to the original userAgent because starting a new session modified it
$this->Session->write('Config.userAgent', $_REQUEST['userAgent']);
// We delete the flash cookie, forcing it to restart this whole process on each request
setcookie(Configure::read('Session.cookie'), '', time() - 42000, $this->Session->path);
}
then you have to pass these 2 params in each flash call to this controller:
param: 'userAgent' -> value: '$this->Session->read('Config.userAgent')'
param: 'sessionId' -> value: $this->Session->id()
http://blogs.bigfish.tv/adam/2008/04/01/cakephp-12-sessions-and-swfupload/
This is specifically for swfUpload but the process of appending the session_id to the urls and the settings for checkAgent and session security are covered and should help point you in the right direction.
Flash doesn't send the cookie along with its requests, that's why Cake doesn't log it in. The way I do it is: you need to somehow pass $this->Session->id() along with your flash requests. That is probably the hardest part because some flash application doesn't let you tag some info along in the request. Then write a component (FlashComponent, or whatever you want to call it) that check if it's a flash request, then look for the session id in its request and set the session id. You need to include this component before 'Auth': so var $components = array('Flash','Auth',...) to intercept the request before Auth does.
Or you can set Auth->allow list, but then you will expose these actions to non-authorization, and the action won't know who the current logged in user is (unless you can pass something in the flash request, in that case, use my first solution).