Web Service Authentication

Hi,

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;

		}

	}

}

?>



There are some built in modules in YII…try YII-user, or Rights modules …rights

I modified my code.




<?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?

Thank you, Martin.

Maybe you can do this check in a filter, or override CController::beforeAction and do the check there?

hi MPeli :

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 :lol:

i know about some people do it as my described ,but haven’t do it myself :lol: and glad to hear others voices

Hello,

today I had the same problem and came up with following solution: http://balrok.com/code/yii_soap_authentication.htm

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.