I am new to Magento Web-Service and have to expand it.
The Webservice shell be able to login an customer, give me the session cookie back so that I can redirect to a file that sets the cookie again, redirects me and I can see my cart and proceed to checkout on the Magento Store.
The Problem:
Magento creates a cookie (that contains the session id or whatever, ive tried to set this cookie manual and them im logged in) instead of setting a session when the customer logs in.
I've tried now for several hours to get this cookie that is set by magento in my magento web-service. It seems the cookie isn't set when i call
$session = Mage::getSingleton('customer/session');
return $session->getCookie()->get('frontend');
Heres my complete Code:
Magento Webservice Api:
<?php
class Customapi_Model_Core_Api
{
public function __construct()
{
}
public function checkout($user, $cart)
{
$ret['cookie'] = $this->login($user);
//$coreCookie = Mage::getSingleton('core/cookie');
//$ret['cookie'] = $_COOKIE['frontend'];
return $ret;
}
function login($user)
{
Mage::getSingleton('core/session', array('name'=>'frontend'));
$session = Mage::getSingleton('customer/session');
try
{
$session->loginById($user['id']);
}
catch (Exception $e)
{
}
return $session->getCookie()->get('frontend');
}
}
?>
Heres my Api Call in Php:
<?php
$teambook_path = 'http://localhost/magento/';
$soap = new SoapClient('http://localhost/magento/api/?wsdl');
$soap->startSession();
$sessionId = $soap->login('ApiUser', 'ApiKey');
$userSession = $soap->call(
$sessionId,
'customapi.checkout',
array(
array(
'id' => 1,
'username' => 'Ruf_Michael#gmx.de',
'password' => '***'
),
array(
)
)
);
echo '<pre>';
var_dump($userSession)."\n";
if(isset($userSession['sid']))
echo ''.$userSession['sid'].''."\n";
echo '</pre>';
$soap->endSession($sessionId);
?>
Thanks for every help!
MRu
Sorry i am writing an answer but the comment box denied me to write more than ... letters.
I've tried both codes you posted and all I get is an empty Array or Bool false.
I wrote a static function:
private static $cookie = array();
public static function cookie($key, $value)
{
if($key == 'frontend') {
self::$cookie['key'] = $key;
self::$cookie['value'] = $value;
}
}
that is called in Mage_Core_Model_Session_Abstract_Varien::start and I got the frontend cookie value:
Customapi_Model_Core_Api::cookie($sessionName, $this->getSessionId());
in line 125.
But that didnt solve my basic Problem:
The Session created in the Api call can't be restored, although it is set to the correct value.
Thanks for your help!
You can get an array of all your cookies with the following command:
Mage::getModel('core/cookie')->get();
The frontend cookie can be retrieved like this:
Mage::getModel('core/cookie')->get('frontend');
From your commented code I can see that you already knew that.
As far as I know, when you log in a user, Magento doesn't just create a new session id, it uses the session id of the active connection (which is generated by PHP itself). You are logging in the user and associating him to the session that your API client just created with Magento. Thus, the code that you have commented seems to be correct for what you are trying to achieve.
Now you should just need to get the returned session id and use it in your new request as the 'frontend' cookie.
Edit (a second time)
Magento has different sessions inside a single PHP session which it uses for different scopes. For instance, there is the core scope, the customer scope, etc. However, the customer scope is also specific to a given website. So, you can have a customer_website_one and a customer_website_two scope.
If you want to log in your user, you have to tell Magento in which website that is. Take the following code as an example
// This code goes inside your Magento API class method
// These two lines get your website code for the website with id 1
// You can obviously simply hardcode the $code variable if you prefer
// It must obviously be the website code to which your users will be redirected in the end
$webSites = Mage::app()->getWebsites();
$code = $webSites[1]->getCode();
$session = Mage::getSingleton("customer/session"); // This initiates the PHP session
// The following line is the trick here. You simulate that you
// entered Magento through a website (instead of the API) so if you log in your user
// his info will be stored in the customer_your_website scope
$session->init('customer_' . $code);
$session->loginById(4); // Just logging in some example user
return session_id(); // this holds your session id
If I understand you correctly, you now want to let the user open a PHP script in your server that sets the Magento Cookie to what you just returned in your API method. I wrote the following example which you would access like this: example.com/set_cookie.php?session=THE_RETURNED_SESSION_ID
<?php
// Make sure you match the cookie path magento is setting
setcookie('frontend', $_GET['session'], 0, '/your/magento/cookie/path');
header('Location: http://example.com');
?>
That should do it. Your user is now logged in (at least I got it to work in my test environment). One thing you should keep in mind is that Magento has a Session Validation Mechanism which will fail if enabled. That's because your session stores info about which browser you are using, the IP from which you are connecting, etc. These data will not match between calls through the API methods and the browser later. Here is an example output of the command print_r($session->getData()) after setting the session in the API method
[_session_validator_data] => Array
(
[remote_addr] => 172.20.1.1
[http_via] =>
[http_x_forwarded_for] =>
[http_user_agent] => PHP-SOAP/5.3.5
)
Make sure you turn of the validation in the Magento Admin in Settings > Configuration > General > Web > Session Validation Settings
Related
Having an issue with the Facebook PHP API/SDK, let me begin by describing:
I made a website where you can enter 'quotes' (short messages) and then after saving the quote in the website (read; too a mysql db behind it) you can decide to share the quote with Facebook to be able to win stuff.
The flow (how it should work) is this:
User opens my site, PHP gets a login url from the Facebook PHP API/SDK
PHP reads & memorizes the 'state' url variable from the login url from the above step (which several sites mention would be a valid way to identify users)
A user saves a quote, I store the state variable from above in the db record of that quote so I can use it again later to match returning users with saved quotes.
The user decides to want to win, so he/she clicks the Facebook share button, which points their browser too the Facebook login url from step 1
The user's browser is now looking at some Facebook pages where they have to allow access for the app and allow that it can post to their wall
Once the user has given said access, they return to the callback url of my site (which happens to be the same as the 'origin url' from step 4)
PHP on my site finds the state variable in the returned url variables(?), goes trough the database to find a matching quote record, and if found, it stores some Facebook user data (userid and path to avatar) in the related db record.
PHP continues to post their earlier saved quote to the user's Facebook wall.
In essence, things work, but the most important bit, step 7, identifying who comes back via the state variable, does not. The problem is that I just get some lengthy 'code' GET variable back, and not the 'state' GET variable, and nowhere in the API docs or on StackOverflow or via Google do I find how to change it so I do get the 'state' GET variable returned again...?
So to recap, basically what I'm looking for is an ability to send some sort of identifier to Facebook that then gets included in the callback-url, and to my knowledge, that's what the 'state' variable seems best for, if it would work that is.
I'm using the currently latest API (facebook-php-sdk-v4-5.0.0.zip from this morning) Below I've shared all relevant code I use to interface with the Facebook PHP API/SDK, all this code resides in the index.php in public_html dir of my site, the callback url of my app is this same index.php
This code is pieced together from several examples, and essentially works, I just don't get the needed state variable back.
require_once __DIR__ . '<PATH-TOO-FB-API>/src/Facebook/autoload.php';
session_start();
$fbStateCode = ""; // used to memorize state code
$fbLoginUrl = ""; // user to memorize login url
// Init the API
// If you go end up testing this, dont forget too change <APP-ID> & <APP-SECRET>
$fb = new Facebook\Facebook([
'app_id' => '<APP-ID>',
'app_secret' => '<APP-SECRET>',
'default_graph_version' => 'v2.4',
'default_access_token' => isset($_SESSION['facebook_access_token']) ? $_SESSION['facebook_access_token'] : '<APP-ID>|<APP-SECRET>'
]);
// login helper
$helper = $fb->getRedirectLoginHelper();
// try get an accesstoken (wich we only have if user returned from facebook after logging in)
try {
$accessToken = $helper->getAccessToken();
} catch(Facebook\Exceptions\FacebookResponseException $e) {
//echo 'Graph returned an error: ' . $e->getMessage(); // When Graph returns an error
} catch(Facebook\Exceptions\FacebookSDKException $e) {
//echo 'Facebook SDK returned an error: ' . $e->getMessage(); // When validation fails or other local issues
}
if (isset($accessToken)) {
// User is logged in!
try {
// Now we look up some details about the user
$response = $fb->get('/me?fields=id,name');
$facebook_user = $response->getGraphUser();
exit; //redirect, or do whatever you want
} catch(Facebook\Exceptions\FacebookResponseException $e) {
//echo 'Graph returned an error: ' . $e->getMessage();
} catch(Facebook\Exceptions\FacebookSDKException $e) {
//echo 'Facebook SDK returned an error: ' . $e->getMessage();
}
// if facebook_user has an id, we assume its a user and continue
if(isset($facebook_user['id'])) {
$avatarUrl = "http://graph.facebook.com/".$facebook_user['id']."/picture";
// THE BELOW 8 LINES HANDLE LOADING A QUOTE FROM DB WITH MATCHING STATE AND SAVING
// ADDITIONAL DATA TOO IT, THE ONLY ISSUE HERE IS THAT $_GET['state'] DOESNT EXIST
// THE REST OF THIS PROCESS HAS ALREADY BEEN TESTED AND PROOFED TO BE WORKING
$curr_quote = Quotes::getQuoteByFbStateCode($_GET['state']);
$curr_quote_data = $curr_quote->getData();
$curr_quote->updateData(array(
"fb_access_token" => $accessToken,
"fb_uid" => $facebook_user['id'],
"fb_avatar_path" => $avatarUrl
));
// Save it
if($curr_quote->save()) { // Success! quote in db was updated
// Now that we are logged in and have matched the returned user with a saved quote, we can post that quote too facebook
$_SESSION['facebook_access_token'] = (string) $accessToken; // storing the access token for possible use in the FB API init
// This is the data we post too facebook
$msg_data = array (
'message' => $curr_quote_data['quote']
);
$response = $fb->post('/me/feed',$msg_data,$accessToken); // do the actual post (this, like everything else besides state variable, works)
} else { // Fail! quote in db was NOT updated?!
// handle errors
}
}
} else {
// User is NOT logged in
// So lets build up a login url
$permissions = ['public_profile','publish_actions']; // we want these permissions (note, atm im the only tester, so while the app still needs to be reviewed for the 'publish_actions' permission, it works cuz i own the app and test with same fb account)
$fbLoginUrl = $helper->getLoginUrl('http://<WEBSITE-URL>/index.php', $permissions); // get the login url from the api providing callback url and permissions array
$fbLoginUrlParams = array();
parse_str($fbLoginUrl, $fbLoginUrlParams); // store the url params in a new array so that we can (read next comment below)
$fbStateCode = $fbLoginUrlParams['state']; // read out and store the state url variable
}
Below here is logic for saving a new quote to database based on user interaction, and making use of $fbStateCode, this part of the process functions fine as well, quotes get saved with their own unique state values like they should.
So that's the story, I'm trying to do something which I'm pretty sure isn't anything special, it's just poorly documented or something?
Ended up rewriting the lot, now it works fine, not sure whats different now vs what i had, so cant really provide an awnser for others running into similair issues, sorry bout that.
#Cbroe; forwarded the headsup you gave me too the customer, got literally told 'not our problem, but the problem of the company that made the concept', so time will tell if this ever even goes online lol, still, thanks for the headsup :P
Read the docs about the state variable here:
The state parameter is a value which is provided by the client prior to the login wen redirecting to the login url. If it's available then, it get's send back in the callback url.
So, unless you provide any state variable in your step1, you won't get any from FB (and any other oAuth2 implementing API). There will not be any "magic" making that appear other than providing it with a feasable state of your PHP app in step 1.
Actually, the state parameter is to give any client the ability to restore a context, whenever the callback "happens". So the content of the state variable may be a session id, a user id or any other value which helps restoring the (php)apps context again after receiving a callback
EDIT
I assume, that the state variable needs to be added somewhere in this function here:
$helper = $fb->getRedirectLoginHelper();
I'm getting a weird behavior I can't fully explain. Consider this controller code:
public function actionCreate()
{
$order = new Order();
$this->performAjaxValidation($order, null, 'order-form');
if (isset($_POST['Order'])) {
$order->attributes = $_POST['Order'];
if ($order->save()) {
Yii::app()->session['order_id'] = $order->id;
$this->redirect('confirm');
}
}
$this->render('create', array('order' => $order));
}
public function actionConfirm()
{
$order_id = Yii::app()->session['order_id'];
if(!$order_id) {
throw new CHttpException(404, 'Order not found');
}
$order = $this->loadModel($order_id);
$this->render('confirm', array('order' => $order));
}
So first I create the order, then if creation is successful, there is a redirect to the confirmation page. Order id is saved to the session so the customer can see the order they created. Why can't I make a redirect to "confirm($order_id)" page? Because 1. Order contains user submitted data and it can't be public, only user created that order can view it on the confirmation page, and 2. There is no authentication there, users are not required to log in.
The weird thing is, most of the time it works fine, but sometimes (1 time out of 5 approx) the order id is not saved in session. If the session is fresh, then it will end up showing 404. If it's not and you create several orders, it will evenually show the previous order data (next order is saved fine).
I would suggest that something is wrong with the session component itself (I'm using CDbHttpSession). But everything else (session related stuff) is working fine on the site (backend auth never fails, flash messages always show up).
Any ideas where should I look?
Okay, it appears pretty obvious now. The reason was classic session race condition: session auto start, performAjaxValidation reads session before order_id is set, then saves session without order_id after order_id is set but before actionConfirm is called. Native PHP sessions are blocking so this situation won't happen but db sessions are not.
I am connecting an other application to create and update customers in Magento 1.9 with SOAP. Because I want the passwords to stay exactly the same customers will be forced to change the password in the other application. After change I want the password to be changed in Magento Through the SOAP connection, but I can't get it working. After the request I am getting "bool(true)" but nothing seems to be changed.
Am I doing something wrong, or are there restrictions in Magento.
My code:
<?php
//ensure you are getting error output for debug
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors',1);
$client = new SoapClient('http://www.mymagentosite.com/api/v2_soap/?wsdl');
// If some stuff requires api authentification,
// then get a session token
$session = $client->login('apiuser', 'apikey');
// CustomerID search
$params = array('complex_filter'=>
array(
array('key'=>'email','value'=>array('key' =>'eq','value' => $email)),
),
);
$result = $client->customerCustomerList($session, $params);
var_dump ($result);
$customerID = $result[0]->customer_id;
// echo $customerID;
// Update Customer
$result2 = $client->customerCustomerUpdate($session, $customerID, array('password' => 'newpassword'));
var_dump ($result2);
It's very strange.
The doc (and the wsdl) wants you to pass the password parameter.
I've tried to investigate in the code (Magento 1.7 series).
Before setting the values you have passed through the API call, in the corresponding update() function of the app/code/core/Mage/Customer/Model/Customer/Api.php file, there is this code:
foreach ($this->getAllowedAttributes($customer) as $attributeCode=>$attribute) {
if (isset($customerData[$attributeCode])) {
$customer->setData($attributeCode, $customerData[$attributeCode]);
}
}
So, I've tried to print the allowed attributes, and password is not present. What is present is password_hash, but this field is not present in the doc, and, more important, is not present in the wsdl.
Just to make a test, I've tried to pass the md5 value of the password I wanted to pass as the password argument, then, in the _prepareData function that is called in the update function, I've added this line of code:
$data['password_hash'] = $data['password'];
The result is that the password is successfully changed, and I can login with the new password.
Now, of course this is not the way to proceed.
First of all, I'm changing a core file.
Then, probably, it's possible to update the list of the allowed attributes adding the password attribute, but you have to remember to build the md5 version of the password, not the "clear text" one, and in any case rename it to password_hash somewhere.
Another solution is to customize (not in core/, again) the wsdl parameters related to the complex type customerCustomerEntityToCreate, adding password_hash.
I'm trying to make a custom form in Drupal. This form call a web service and will receive some Json. in this Json response, I have three information (or an error) :
1. User name
2. User language
3. User custom Token (special for another webapp)
Those three info must be placed in session (or anything else) and will be shown in some webpages.
In example, on every page, It will be written "Hello Mr John", and on some web pages I want to show a icon + link with the custom Token to access to another web application, ...
We already have a lots of users, and we don't want to have all the users in the drupal DB.
How can I Do? I'm using Drupal 7
Thanks in advance
I wrote this function to deal with session in Drupal
function _mySite_session($key, $value = NULL)
{
static $storage;
if ($value)
{
$storage[$key] = $value ;
$_SESSION['mykey'][$key] = $value ; // I use 'mykey' as a session key because in case some other module uses 'type' in $_SESSION. So try to make it unique.
}
elseif (empty($storage[$key]) && isset($_SESSION['mykey'][$key]))
{
$storage[$key] = $_SESSION['mykey'][$key];
}
return $storage[$key];
}
To set a session variable:
_mySite_session("USERNAME", "Mr. John");
And to retrieve the value of the session variable:
$username = _mySite_session("USERNAME");
I need to create a system plugin (no auth plugin!) where a user which logges into the frontend automaticaly gets logged in the backend too.
(The user has the rights to log into the backend via /administrator.)
I try to do it via the very basic code you see below, the result is positive, but if i go to the backend the user still needs to log in.
In the session table the backend session row is set, but the "guest" field is set to 1 instead of 0 and the userid is set to 0 instead of the correct id.
How can this be done?
function onAfterInitialise() {
if(JFactory::getUser()->get('id')) { // logged in?
$credentials = array();
$credentials['username'] = "walter"; // hardcoded first
$credentials['password'] = "123"; // hardcoded first
$options = array();
$options['action'] = 'core.login.admin';
$result = $app->login($credentials, $options); // this seams to work
if (!($result instanceof Exception)) {
$app->redirect("www.bummer.de");
}
}
Apart from this being a very bad idea, as mentioned in this question Joomla! is implemented as two applications a front-end (/index.php) and back-end application (/administrator/index.php).
In the code provided you don't show where $app is initialised so I'm guessing that it's probably something like $app->JFactory::getApplication('site');.
To login to the admin app you need to get it rather than the front-end client app e.g.
$adminApp->JFactory::getApplication('administrator');
$result = $adminApp->login($credentials, $options);
n.b. this is untested code just typed in to stack overflow... it should be right.