I have created a web service using yii. I was wondering what is the best way to implement an authentication process. I tried something like this but I did not achieve any satisfactory result.
Thank you, Martin
<?php
class WebServiceController extends CController
{
public function actions()
{
return array(
'index'=>array(
'class'=>'CWebServiceAction',
),
);
}
/**
* @param string the username
* @param string the password
* @return boolean
* @soap
*/
public function login($username, $password) {
$_identity = new UserIdentity($username,$password);
if($_identity->authenticate()) {
Yii::app()->user->login($_identity);
return true;
}
else {
return false;
}
}
public function filters()
{
return array(
'accessControl',
);
}
public function filterAccessControl($filterChain) {
if(Yii::app()->user->getId() == 'WebService' or (Yii::app()->controller->action->id == 'index' or Yii::app()->controller->action->id == 'login'))
{
$filterChain->run();
return true;
}
else
{
return false;
}
}
}
?>
<?php
class WebServiceController extends CController
{
public function actions()
{
return array(
'index'=>array(
'class'=>'CWebServiceAction',
),
);
}
/**
* @param string the username
* @param string the password
* @return boolean
* @soap
*/
public function login($username, $password) {
$_identity = new UserIdentity($username,$password);
if($_identity->authenticate()) {
Yii::app()->user->login($_identity);
return true;
}
else {
return false;
}
}
/**
* @param integer the id
* @return string
* @soap
*/
public function getData($rid)
{
if(Yii::app()->user->getId() == 'MyUser') { // I have ta add this code to every function
$model = Data::model()->findByPk($rid);
return CJSON::encode($model);
}
}
?>
I have no experience with SOAP and I am not sure how to create authentication in the right way. The solution above seems to work. If you are not logged in a function getData returns nothing. But I have to login again with next request.
I have to include this condition if(Yii::app()->user->getId() == ‘MyUser’) into each function which returns data.
Is there any example how to implement an authentication process properly?
the webservice is different from the normal browser access . so every request you should validate it .
for normal browser access the cookie/get/post will pass the php’s “session_id()” so we can use session to verify the “user” . but for webservice the sessionId is gone .
one solution is mimic the OAuth2 mechanism . when the first login passed we give the client(apiClient) a accessToken . only the first access should pass the login info (clientKey or some private key combinations ) , after logined the following access we just validate the accessToken(surely you should keep it in database )
the solution is a little difficult . you should first create a api_client table which may contain all registered client (who can access your webServices ,and a specified client have a accessKey )
for accessing : validate the access in __construct , beforeAction (or a filter) of the WebServiceController
i know about some people do it as my described ,but haven’t do it myself and glad to hear others voices
The idea is to first authenticate and get a unique session key back. This sessionkey is a serverside mapping to a user.
The client has to use this sessionkey in all further requests as first parameter.
In case I take my page one day down I will post the code here too:
Client:
$client = new SoapClient('http://127.0.0.1/yii/test/api/soap/wsdl');
// login using username and password
$session = $client->login('testuser@example.com', 'secretPassword');
// calling some other soap functions using our retrieved session key
$client->furtherMethod($session, 123);
$client->furtherMethod2($session, 'abc');
$client->furtherMethod3($session);
Controller:
Yii::import('user.components.UserIdentity');
class SoapController extends Controller
{
/**
* @param string the username
* @param string the password
* @return string a sessionkey
* @soap
*/
public function login($name, $password)
{
$identity = new UserIdentity($name, $password);
$identity->authenticate();
if ($identity->errorCode == UserIdentity::ERROR_NONE)
Yii::app()->user->login($identity, 3600);
else
throw new SoapFault("login", "Problem with login");
$sessionKey = sha1(mt_rand());
Yii::app()->cache->set('soap_sessionkey'.$sessionKey.Yii::app()->user->id, $name.':'.$password, 1800);
return $sessionKey;
}
/**
* authenticates a user via the sessionid
* throws an exception on error
*/
protected function authenticateBySession($sessionKey)
{
$data = Yii::app()->cache->get('soap_sessionkey'.$sessionKey.Yii::app()->user->id);
list($name, $password) = explode(':', $data);
if ($name)
{
$identity = new UserIdentity($name, $password);
$identity->authenticate();
if ($identity->errorCode == UserIdentity::ERROR_NONE)
Yii::app()->user->login($identity, 3600);
}
// happens when session is invalid or login not possible (deleted, deactivated)
if (!Yii::app()->user->id)
throw new SoapFault('authentication', 'Your session is invalid');
}
/**
* @param string the session key
* @param int random stuff
* @return int current user id
* @soap
*/
public function furtherMethod($session, $bla)
{
$this->authenticateBySession($session);
return Yii:.app()->user->id;
This basic example has a security problem because it stores the password in the cache. My solution was to store just the name and implement inside UserIdentity->authenticate a parameter which says, that we don’t need to check the password
What does the "/wsdl" mean in the "<localhost>/yii/test/api/soap/wsdl" request?
When calling my own WS, I get the following message:
Fatal error: Uncaught SoapFault exception: [SOAP-ENV:Server] Call to a member function set() on a non-object in soapClient.php:5 Stack trace: #0 soapClient.php(5): SoapClient->__call('login', Array) #1 soapClient.php(5): SoapClient->login('test', 'test') #2 {main} thrown in soapClient.php on line 5
But if I call the URL, I get the WSDL file in a correct way.