How to do authentication using SOAP?

40,475

Solution 1

An easier way would be to authenticate on the first query, build a session record on the server side containing the remote IP address and a token that you give to the client as an authToken. Then have the client pass this authToken in future queries. This authToken has to match the internal session data you keep about the client, but would allow you to avoid having to make round-trips to the database just to do authentication.

That said, @Marcus Adams has a good point below with regard to stateless-ness. There are people out there pushing all sorts of SOAP security models. WS-Security is the current state of the art, here. They all work by putting authentication information in the SOAP header - after all, that's why a SOAP message contains both a header and a bodypart.

Solution 2

Here's a simple example of how I use an API validation in the header:

file portfolio-lookup-client.php

<?php
ini_set("soap.wsdl_cache_enabled", "0"); // disabling WSDL cache
class portfolioLookupAuth 
{ 
    public $apiKey; 
    public function __construct($key) 
    { 
        $this->apiKey = $key; 
    } 
} 
$apiKey = "123456"; 
$url = 'http://mysite.com/php5soap/portfolio-lookup.wsdl';
$client = new SoapClient($url, array("trace" => 1, "exception" => 0)); 

// Create the header 
$auth  = new portfolioLookupAuth($apiKey); 
// SoapHeader::__construct ( string $namespace , string $name [, mixed $data [, bool $mustunderstand [, string $actor ]]] ) 
$header = new SoapHeader($url, "APIValidate", $auth, false);   

  try {

    $result = $client->__soapCall("getPortfolioByName", array("portfolioName" => "WQAM"), NULL, $header);       
    print_r($result);

    print "<pre>\n"; print "Request :\n".htmlspecialchars($client->__getLastRequest()) ."\n";
    print "Response:\n".htmlspecialchars($client->__getLastResponse())."\n"; print "</pre>";    

  } catch (SoapFault $exception) {

    echo 'Exception Thrown: '.$exception->faultstring.'<br><br>';  

  }

?>

file portfolio-lookup-server.php

<?php
ini_set("soap.wsdl_cache_enabled", "0"); // disabling WSDL cache

class PortfolioLookupService {

    private $apiKey = '123456';

    private $portfolios = array(
            'WPOW' => 'Power 96 party station.',
            'WQAM' => 'Sports radio site.',
            'WJBR' => 'Cool sites for bands.',
            'WKIS' => 'Kiss Country 2',

  );

  public function APIValidate($auth){

    if($auth->apiKey != $this->apiKey){
        throw new SoapFault("Server", "Incorrect key");
    }

  }

  function getPortfolioByName($portfolioName) {
    //print_r($portfolioName); exit();
    if (isset($this->portfolios[$portfolioName])) {
      return $this->portfolios[$portfolioName];
    } else {
      return 'Portfolio name "'.$portfolioName.'" not found.';
      //throw new SoapFault('code', 'string', 'actor', 'detail', 'name', 'header');
      throw new SoapFault("Server","Unknown Name '$portfolioName'.");      
    }
  }  

  function getPortfoliosAll() {
      return $this->portfolios;
  }    

}

$server = new SoapServer("portfolio-lookup.wsdl");
$server->setClass("PortfolioLookupService");
$server->handle();

?>

file portfolio-lookup.wsdl

<?xml version ='1.0' encoding ='UTF-8' ?>

<definitions name='PortfolioLookup'

  targetNamespace='http://example.org/PortfolioLookup'

  xmlns:tns='PortfolioLookup'

  xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'

  xmlns:xsd='http://www.w3.org/2001/XMLSchema'

  xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/'

  xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'

  xmlns='http://schemas.xmlsoap.org/wsdl/'>

<message name='getPortfolioByNameRequest'>
  <part name='portfolioName' type='xsd:string'/>
</message>
<message name='getPortfolioByNameResponse'>
  <part name='Result' type='xsd:string'/>
</message>


<message name='getPortfoliosAllRequest'>
  <part name='portfolioName' type='xsd:string'/>
</message>
<message name='getPortfoliosAllResponse'>
  <part name='Result' type='xsd:array'/>
</message>


<message name='APIValidateRequest'>
<part name='apiKey' type='xsd:string'/>
</message>
<message name='APIValidateResponse'>
<part name='testReturn' type='xsd:string'/>
</message>



<portType name='PortfolioLookupPortType'>

  <operation name='getPortfolioByName'>
    <input message='tns:getPortfolioByNameRequest'/>
    <output message='tns:getPortfolioByNameResponse'/>
  </operation>

  <operation name='getPortfoliosAll'>
    <input message='tns:getPortfoliosAllRequest'/>
    <output message='tns:getPortfoliosAllResponse'/>
  </operation>

    <operation name='APIValidate'>
    <input message='tns:APIValidateRequest'/>
    <output message='tns:APIValidateResponse'/>
    </operation>

</portType>

<binding name='PortfolioLookupBinding' type='tns:PortfolioLookupPortType'>

  <soap:binding style='rpc'
    transport='http://schemas.xmlsoap.org/soap/http'/>


  <operation name='getPortfolioByName'>
    <soap:operation soapAction='urn:PortfolioLookup#getPortfolioByName'/>
    <input>
      <soap:body use='encoded' namespace='urn:PortfolioLookup'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </input>
    <output>
      <soap:body use='encoded' namespace='urn:PortfolioLookup'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </output>
  </operation>


  <operation name='getPortfoliosAll'>
    <soap:operation soapAction='urn:PortfolioLookup#getPortfoliosAll'/>
    <input>
      <soap:body use='encoded' namespace='urn:PortfolioLookup'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </input>
    <output>
      <soap:body use='encoded' namespace='urn:PortfolioLookup'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </output>
  </operation>  




</binding>

<service name='PortfolioLookupService'>

  <port name='PortfolioLookupPort' binding='PortfolioLookupBinding'>
    <soap:address location='http://mysite.com/php5soap/portfolio-lookup-server.php'/>
  </port>

</service>

</definitions>

Solution 3

Having the user send the username and password with each request is the way that I've seen most SOAP interfaces implemented. Actually, I've not seen any other implementation other than the API key idea, which is just trading a Username and Password for some other token.

SOAP interfaces should be stateless, like HTTP, so this seems like a normal consequence.

Solution 4

Define a custom SOAP header and exchange authentication credentials in the header. Read values from header and authenticate.

Share:
40,475
YD8877
Author by

YD8877

Updated on July 16, 2022

Comments

  • YD8877
    YD8877 almost 2 years

    How do I authenticate users with SOAP?

    Will I have to require the user to send his username and password with every SOAP request and I authenticate him against the database?

    Doesn't that seem to cause unnecessary queries?