Hold Guzzle Cookies across multiple requests - php

I'd like to create a proxy for a specific site so users, which can't access that site (due to the sites IP being blocked by their ISP for instance), can access it through my proxy. I would use Guzzle to make HTTP requests, exchange all links to my own site and then display it.
I know this is not exactly ideal, but for a start it seemed to work and I don't expect hundreds of people to use it. It should just be in place, in case someone does need it.
The problem I am having is cookies. The other site obviously uses sessions/cookies to keep the users logged in. Basically there's nothing you can do without being logged in. I already pass all form data and such to the HTTP request, but cookies keep throwing me out.
protected function makeRequest($path, $parameters)
{
$cookieJar = new CookieJar;
$client = new Client(['base_uri' => self::$staticURL]);
$response = $client->request($parameters['method'], self::buildUrl($path), [
'form_params' => Request::all(),
'cookies' => $cookieJar,
]);
// Session::flash can only flash strings, but CookieJar is not serializable.
// Session::flash('cookie', $cookieJar);
return self::replaceURLs($parameters['original'], self::$staticURL, $response->getBody(), $parameters['base']);
}
As you can see I am passing a CookieJar instance to the request, as it was mentioned in the documentation. With this, I am able to log into the site, but as soon as I click another link, it throws me back out. So I assume it doesn't save the cookie across multiple requests.
Which ways do I have to do that?

I thought the cookies option required an instance of type CookieJar, which is not the case. What it requires is a CookieJarInterface. Looking further into the Guzzle library I found the FileCookieJar, which is saved in a file.
I now generate an ID for each session and write the cookies into a file using that ID. That way each user of my page has the cookies saved and they get loaded on each subsequent request.
protected function makeCookiePath()
{
$sessionId = $this->request->session()->get('cookie.id', str_random(40));
$this->request->session()->put('cookie.id', $sessionId);
return storage_path('cookies/' . $sessionId);
}
protected function makeRequest($path, $parameters)
{
$cookieJar = new FileCookieJar($this->makeCookiePath(), true);
// ...
}

Related

How to implement authentication on a REST architecture with Parse

I am currently redoing a legacy web application that uses the PHP Parse SDK, and I am in the login authentication part. In the old application, we used $ _SESSION and ParseToken when doing ParseUser::signIn() and ParseUser::currentUser() to check if you have a session with a valid token, however the new application is being made using the REST architecture, where one of the REST concepts is that the server must not keep state, that is, be stateless, and in that case it would be the client that would have to send the necessary data.
When searching the internet and forums, I saw that it is common for developers to authenticate with JWT, where the client would make a request for a server's route and the server would return a token, and through that token authentication would take place.
I even implemented something using Firebase / jwt-php, where the client [Postman] makes a request for the route /login sending via body [username, password] and in case of success, returns the token to be used in secure route requests.
NOTE: Code is as simple as possible, without validation and cleaning just to show the example.
Action /login
$username = $request->getParsedBody()['username'];
$password = $request->getParsedBody()['password'];
$userAuthenticated = ParseUser::logIn($username, $password);
$payload = [
'data' => $userAuthenticated,
'exp' => time() + 3600
];
$token = JWT::encode($payload, $_ENV['JWT_SECRET_KEY']);
echo json_encode(['token' => $token]);
And the protected routes have a middleware that checks if the time has expired, and if this has happened, an exception with a 401 code is launched.
So far so good, authentication works, the problem I don't know if it's right to do it this way, since I need to give a ParseUser::logIn(), just to generate a session in the database and I don't even use it this session to do some authentication, with the exception of operations in the bank, because from what I saw in the documentation, if there is no valid session in the database, the application will return invalid session token error and also when making the request for another route ParseUser::currentUser() returns null, and this may be a problem in the future.
Does anyone have any idea how I can implement authentication for a REST application made in PHP? I appreciate the help !!
I believe the easiest way would be just replacing the default session storage (which uses $_SESSION) to something else that stores the session in, for example, Redis. Reference: https://docs.parseplatform.org/php/guide/#session-storage-interface
But the way you are doing should also work. You will only have to make sure that, every time that a request comes, you will decode the JWT, get the Parse Session token from there, and use ParseUser::become to set the current user: https://docs.parseplatform.org/php/guide/#setting-the-current-user

Settings a Cookie by POSTing to a RESTful Magento controller

I am building an application which will eventually reside on the same domain where another application resides; both of which are written in PHP. One is a Laravel application and the other a Magento 1.9 store.
To authenticate the user, the Laravel application requires that a certain cookie be set by the Magento store's response, subsequently retrieved and parsed, all before authentication may continue.
My current strategy is a POST to a custom controller which delivers multiple Set-Cookie headers from the Magento store.
The one I need is something like:
Set-Cookie: auth_token=TheValueWeNeedToContinueAuthenticating; domain='.mydomain'; ...
The server I am testing on is an in-house staging environment. The server's VHost is set to Laravel's public directory, as usual.
The Magento store is on a different server however the TLD is the same .mydomain
I have verified the response in Postman, I am indeed returning the cookie with the correct Set-Cookie in place, however it is not visible in my Laravel application as the other cookies from Magento, are. The other cookies were verified within the response as I dumped Guzzle's CookieJar and still received all but the cookie I am looking for.
I am using PHP's Guzzle HTTP Library to post from a Laravel 5.6 app using PHP 7.1
The Magento1.9 store unfortunately uses PHP/5.6.35
When I dump the HTTP response I am getting the cookies which would normally receive had I actually visited any page within the store.
What else could I check to ensure I am taking to right approach to receiving this cookie? Https is the transmission protocol, and the content-type is x-www-form-urlencoded if that will assist an answer in any way.
Thank you.
UPDATE 1.0 - I was able to get a clean error reading from the request being sent.
{"status":"error","message":"invalid request"}
Here is my Guzzle Post request
$jar = new \GuzzleHttp\Cookie\CookieJar;
// Logging my error output -- I can share this if I must
$debug_file = fopen('../storage/logs/debug.txt', 'a');
try {
$payload = 'Knock, knock';
$url = '/api/for/post';
$client = new Client([
'base_uri'=> 'https://mySubdomain.myDomain.com',
'debug' => $debug_file,
'cookies' => $jar,
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
]
]);
$request = new \GuzzleHttp\Psr7\Request('POST', $url, [], $payload);
$rsp = $client->send($request);
dd($rsp->getBody()->read(1024));
$response->getBody()->read(1024) returns the error message

Log in to an external website and redirect it as a logged-in user

great pleasure someone helps me with the following question, you will see I want to create a site that stores the accounts of the external pages, so that when they enter the user and the password in my application, this one realizes the login in the external web and then redirect the page how logged in users. My question is this, is it possible to do that? I wanted to try it with guzzle, but it could not, then I tried goutte, but I got an error that did not detect the fields, what could I do? Thank you
So users will need to share their username and password with you and you'll be storing that in your DB, and it'll need to be plain text or in a way that is easily converted back into plain text. This is a BIG RED FLAG.
If there is some legitimate reason why you'd be doing this, I'd suggest working with a CookieJar in Guzzle. You could POST a login with the user credentials, and store the resultant cookie, then send that in the headers with any subsequent request for that user.
Some rough code to give you an idea:
$client = new GuzzleHttp\Client;
$filename = $somePath . '/cookies.json';
$jar = new GuzzleHttp\Cookie\FileCookieJar($filename);
// Login
$url = 'http://some_login_url';
$body = [
'user' => $someUser,
'pass' => $somePassword
];
$response = $client->post($url, ['cookies' => $jar, 'body' => $body]);
// Make sure the response is valid here
$jar->save($filename);
// Retrieve a private page for a user
$url = 'http://private_page_url';
$response = $client->get($url, ['cookies' => $jar]);
// Make sure the response is valid here
$html = (string) $response->getBody();
// Do scrapy stuff with HTML
Clearly, this would need a bit of work to make it useful.

Cannot sent cookie with Guzzle request

I have a PHP script that needs to fetch a CSV file from an application. There is an API for the application that allows the script to lot in, which gives the script a session cookie for authentication. I then need to doa GET request to fetch the CSV file (which the API does not support).
Using curl directory works:
$c = curl_init($url);
curl_setopt($c, CURLOPT_COOKIE, 'PHPSESSID=' . $session_id_from_api);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
$csv_file = curl_exec($c);
echo $csv_file;
That fetches the CSV file using the session ID obtained from the API login and passed through a coookie.
Now, I would like to do the same thing using Guzzle, but I just get the login page back instead. This is the code I'm using:
$client = new Guzzle\Http\Client();
$request = $client->get(
$url,
[
'cookies' => ['PHPSESSID' => $session_id_from_api],
]
);
$response = $client->send($request);
echo $response->getBody(true);
That gives me the login page, so the GET to the application is not recognising the session defined by the cookie.
Is there anything else I need to do to ensure the cookie and value I specify is sent to the remote application?
Edit: looking at $request->getRawHeaders(), I see this line in the headers:
cookies: vec6nb1egvvui6op7qr7b0oqf6
That obviously isn't right. The documentation for my version of Guzzle gives this example:
// Enable cookies and send specific cookies
$client->get('/get', ['cookies' => ['foo' => 'bar']]);
which looks to me to be consistent with what I am passing to Guzzle.
Just to be clear, I am not trying to manage cookies in both directions over multiple requests, so there is no need to store any cookies. I have a cookie name and its value (from another source), and I just want to make sure that name and value gets sent to the destination for a single GET request. I'm not trying to "maintain a session", but in a way I am having a session passed to me from another part of the application (not Guzzle) and need to set my Guzzle request up to use it.
Well, this seems to work. Guzzle was not sending the cookie without being sure the domain it was sending it to was correct:
// Set up a cookie - name, value AND domain.
$cookie = new Guzzle\Plugin\Cookie\Cookie();
$cookie->setName('PHPSESSID');
$cookie->setValue($session_id_from_api);
$cookie->setDomain($domain_of_my_service_url);
// Set up a cookie jar and add the cookie to it.
$jar = new Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar();
$jar->add($cookie);
// Set up the cookie plugin, giving it the cookie jar.
$plugin = new Guzzle\Plugin\Cookie\CookiePlugin($jar);
// Register the plugin with the client.
$client->addSubscriber($plugin);
// Now do the request as normal.
$request = $client->get($url);
$response = $client->send($request);
// The returned page body.
echo $response->getBody(true);

Cannot Store Instagram API access_token with PHP

I'm working on building a small web app connecting into the instagram API. Currently using this library on GitHub which makes things a bit easier. I can connect and initially login fine, and the page will display all user data.
But once you refresh the page all the data is lost, and it appears the script can't find my access token anymore. I tried storing this into a PHP session variable - but maybe I'm doing the whole process incorrectly? I just want to keep the same user session throughout an entire website once the OAuth is performed.
You can check out the small app live here: http://spyrestudios.com/demos/instagram-api/index.php
Additionally my callback URL is http://spyrestudios.com/demos/instagram-api/instagammy.php - this is the script which will work right after you connect. But try refreshing the page and all the data is gone! Also here is my bit of code which *should use the library to store the current user's access token:
session_start();
require_once 'Instagram.php'; // the library code
$config = array(
'client_id' => 'f0d225aa955c4bd9ae563f87f831efab', // Your client id
'client_secret' => '377b77afc1274a89bd2df7d77e934689', // Your client secret
'grant_type' => 'authorization_code',
'redirect_uri' => 'http://spyrestudios.com/demos/instagram-api/instagammy.php', // The redirect URI you provided when signed up for the service
);
// Instantiate the API handler object
$instagram = new Instagram($config);
$accessToken = $instagram->getAccessToken();
$_SESSION['InstagramAccessToken'] = $accessToken;
Really struggling for a solution, so I'd appreciate any help I can get. Willing to post examples of my code if needed..
Thanks in advance!
What you have already looks fine (although I have no experience in Instagram), assuming that $accessToken is actually being stored correctly in the session.
But surely at some point you need to feed back in $accessToken so that it can be verified. Like I said, I've not used Instagram before, so I don't know how you would do this.
Just in case you still care..
The problem is that refreshing is hitting the oauth landing page again when you refresh or back, but there is no authorization for it to collect the information from. In theory you could just wrap your setting of the session variable around a check:
$instagram = new Instagram($config);
$accessToken = $instagram->getAccessToken();
if ($accessToken != '')
$_SESSION['InstagramAccessToken'] = $accessToken;
That would stop an unwanted to call to your oauth landing page from clearing the current session value.
But better still would be to have the landing page redirect to a different page for the display. ie have your instagammy.php end there and add a
header("Location: http://spyrestudios.com/demos/instagram-api/dosomethinghere.php");
die();
That way the user won't ever see the "instagammy.php" page in their history and won't be able to go back to it.
This work for me, instead the original file instagammy.php
$instagram = new Instagram($config);
if ($_SESSION['InstagramAccessToken'] == ''){
$accessToken = $instagram->getAccessToken();
$_SESSION['InstagramAccessToken'] = $accessToken;
}else{
$accessToken = $_SESSION['InstagramAccessToken'];
$instagram->setAccessToken($_SESSION['InstagramAccessToken']);
}

Categories