Delegated Authentication Using PHP

Red Argyle logo

The phrase “I wish I had more passwords to remember” fits near the top of a list of things your users will never say. Luckily, Salesforce is kind enough to allow us to use Single Sign On, or SSO, to authenticate users. This enables our users to use authentication credentials they already have for another system to access Salesforce. We recently had to implement SSO for a client using their existing PHP server and the amount of material around this on the Internet was a bit thin, so I thought I’d share our experience in the hopes that it might give others a clear path on how to proceed in this situation.

First I’d like to mention that Salesforce supports a few different different types of SSO authentication. There are 3 methods of setting up SSO in your organization: Federated Authentication, Delegated Authentication, and OpenId Authentication. Each of these have their own benefits, and you can read about the details of each here. In this post, we will be focusing on delegated authentication, or specifically, how to implement delegated authentication using PHP.

Implementation of delegated authentication in Salesforce relies on the existence of a web service that you will need to create. Regardless of which language you choose to use, you will need to set up a web service that can accept a SOAP request from Salesforce and respond with a message indicating whether or not the user was successfully authenticated.

Enabling Delegated Authentication in Salesforce

Delegated authentication is not enabled by default in Salesforce organizations, so the first step is to contact support and request that they activate the delegated authentication feature in your environment. SSO settings can be accessed from the Single Sign-On Settings page located in the Setup section of the site under Security Controls. Once the DA feature has been turned on in your org, your SSO Settings page should look like the one pictured below.

Delegated Authentication

The only section we are concerned with is the one labeled “Delegated Authentication.” There are just two settings for DA:

  • Delegated Gateway URL – This is the url to the web service you will create that will accept SOAP messages from Salesforce.
  • Force Delegated Authentication Callout – This is described in Salesforce documentation as follows: “When this box is unchecked, a call is not made to the SSO endpoint if the login attempt first fails because of login restrictions within the Salesforce organization. If you must record every login attempt, then check this box to force a callout to the SSO endpoint regardless of login restriction failures.”

There is one last setting to change in Salesforce. For a user to be able to log in using SSO they need to have the “Is Single Sign-On Enabled” permission set to true. This is a setting that lives on a permission set.

Ok, we’ve got our org set up and users have the correct permissions. Now we’re going to get language specific. The principle is the same regardless of which language you use. You can provide your users with a link to access Salesforce using this format:

https://login.salesforce.com?un=sampleuser@sample.org&pw=myPassword99

Salesforce will read the username, determine if it is a valid user, and if it is set up to allow SSO. If that’s the case, Salesforce will send a SOAP request to the web service you’ve defined for “Delegated Gateway URL” in the SSO settings (see above), using the username and password provided in the login URL. The password argument does not need to be an actual password, it can be any string that will be used to authenticate your user against another identity provider.

Your web service will need to accept a message in a specific format and respond in a specific format.

Here is the format of a SAOP request that you will need to accept:

gist id: fb183774fd97653bf9f0
<code>
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope
   xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <Authenticate xmlns="urn:authentication.soap.sforce.com">
         <username>sampleuser@sample.org</username>
         <password>myPassword99</password>
         <sourceIp>1.2.3.4</sourceIp>
      </Authenticate>
   </soapenv:Body>
</soapenv:Envelope>
</code>

The authenticate action takes 3 arguments:

  • Username: This will be populated with a valid Salesforce username that was originally sent with the login request.
  • Password: Again, this is the same password that was sent with the original login request. It can be any string that will be used to authenticate your user.
  • SourceIp: The IP address that originated the login request.

This is the format of the response message that Salesforce is expecting:

gist id: 300704d298a44baccb83
<code>
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope 
   xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <AuthenticateResult xmlns="urn:authentication.soap.sforce.com">
         <Authenticated>false</Authenticated>
      </AuthenticateResult>
   </soapenv:Body>
</soapenv:Envelope>
</code>

Populating the authenticated element with “true” will tell Salesforce that the user has been authenticated and can access your organization, while “false” will reject the login attempt and send them to the native Salesforce login screen.

The library we used to build the web service in PHP is called nusoap. It’s dead simple to set up. The first step is to include the nusoap library and create and configure a new server.

gist id: 4ac6378626f66ccae77a
<code>
require_once "nusoap/lib/nusoap.php";
//create the server.
$server = new soap_server;
//set configuration for the web service's WSDL.
$server->configureWSDL('authentication.soap.sforce.com','urn:authentication.soap.sforce.com','',"document");

</code>

Next, we register an action in our web service called “Authenticate.” It will accept: username, password and sourceIp and will return a boolean called Authenticated

gist id: d0ae6d803bf3f0f4b7aa
<code>
//register an action called Authenticate.
$server->register(
   'Authenticate',
   array('username'=>'xsd:string','password'=>'xsd:string','sourceIp'=>'xsd:string'),
   array('Authenticated'=>'xsd:boolean')
);
</code>

Now we will write the method that corresponds to the registered action above. This will take the same 3 arguments defined in the sample request and that were included in the register script above. This method needs to return a boolean, true if authenticated and false if the authentication fails.

gist id: 88aa3f0aa39bb70410d1
<code>
function Authenticate($username, $password, $sourceIp) {
   try{
       $valid = false;
       //Authenticate request here
       return $valid;
   } catch (Exception $e) {
       //write error message a log file $e->getMessage();
       return false;
   }
}
</code>

Lastly, kick the service off by adding these two lines:

gist id: 7f0152886018aa52f063
<code>
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
</code>

That’s it. All that’s left is for you to add the code to the authenticate method that will authenticate the request against your identity provider. Not so bad, right? If you’re interested in mastering this useful process, check out the full script here: https://gist.github.com/zargyle/52621165e3f7e1edbd14.


 

Red Argyle logo
Red Argyle logo

Related Blog Posts