Ok guys. Thanks to my colleague, Rob who got on the Salesforce team at dreamforce to show how this could be done there is Great news!!! You would need to turn Chatter answers on in your SF org and then enable it for your communities(under site settings). Once this is done you can choose the visualforce page you want to use as your change password page. And voila. That's it!!! Just wished SF did better with their documentations for some of their products.
I suspect the issue is around the security token and the Trusted IP Ranges in the Network Access Security Controls.
Something to check, look for failed logins under Administration Setup > Manage Users > Login History
. If you see the Status message "Failed: API security token required" then the requesting Salesforce IP address isn't trusted.
If the IP address that the login request is coming from isn't trusted Salesforce will reject it unless you append the security token on the end of the password.
So your options are:
- Modify the Visualforce page to prompt the user to append their security token. You can't get this in Apex code. It is emailed to the user using the Reset my Security Token link.
- Add the requesting Salesforce servers IP address to the Trusted IP addresses under
Administration Setup > Securtiy Controls > Network Access: Trusted IP Ranges
. You could also check Salesforce IP Address to whitelist
This isn't so applicable anymore, but does provide a sample working version from anonymous apex
It would appear that the sample code isn't building a valid SOAP POST request for the partner API. In particular, it is missing the body element. I modified the way the XML is created and got it working.
The request body should look something like:
<?xml version="1.0" encoding="utf-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header />
<env:Body>
<login xmlns="urn:partner.soap.sforce.com">
<username>username@example.com</username>
<password>UserPasswordqFOSeCuRiTyTokEn9v6DeJysC</password>
</login>
</env:Body>
</env:Envelope>
Here is the hastily modified login Soap message. Note the additional XML declaration and Body plus Header elements in the xmlOutput
.
String LOGIN_DOMAIN = 'login'; //other options: test, prerellogin.pre
String username = UserInfo.getUsername();
String password = 'password'; // + security token
XmlStreamWriter w = new XmlStreamWriter();
w.writeStartElement('', 'login', 'urn:partner.soap.sforce.com');
w.writeNamespace('', 'urn:partner.soap.sforce.com');
w.writeStartElement('', 'username', 'urn:partner.soap.sforce.com');
w.writeCharacters(username);
w.writeEndElement();
w.writeStartElement('', 'password', 'urn:partner.soap.sforce.com');
w.writeCharacters(password);
w.writeEndElement();
w.writeEndElement();
String xmlOutput =
'<?xml version="1.0" encoding="utf-8"?>\n'
+ '<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">'
+ '<Header />'
+ '<Body>'
+ w.getXmlString()
+ '</Body>'
+ '</Envelope>';
w.close();
System.debug(xmlOutput);
HttpRequest request = new HttpRequest();
request.setEndpoint('https://' + LOGIN_DOMAIN + '.salesforce.com/services/Soap/u/22.0');
request.setMethod('POST');
request.setHeader('Content-Type', 'text/xml;charset=UTF-8');
request.setHeader('SOAPAction', '""');
request.setBody(xmlOutput);
//basically if there is a loginResponse element, then login succeeded; else there
// would be soap fault element after body
Boolean verified = (new Http()).send(request).getBodyDocument().getRootElement()
.getChildElement('Body','http://schemas.xmlsoap.org/soap/envelope/')
.getChildElement('loginResponse','urn:partner.soap.sforce.com') != null;
if(verified) { System.debug('Correct password!'); }
else { System.debug('Incorrect password!');
Best Answer
You will need to create a HttpCalloutMock that mocks the response from Salesforce Partner API.
See Testing HTTP Callouts by Implementing the HttpCalloutMock Interface