You are using an older browser that might negatively affect how this site is displayed. Please update to a modern browser to have a better experience. Sorry for the inconvenience!

Password Authentication to Person Accounts Using API Callouts


Introduction: 

In this article, I have explained how an authentication mechanism can be built even for the users of Salesforce when they access Person Accounts. This approach is used to overcome cross-site scripting security gaps.

Process Explanation: 

After the users logged into Salesforce, if they want to access some sensitive data, here, for example, Person Accounts, then the users need to authenticate by reentering a password. So, we have used API Callouts to achieve this.

Enabling Remote site setting is required to use API Callouts. To validate the password for a user, we used our organization (Login, Test) URL to retrieve the password.

This data protection is not applicable to business accounts. It can be accessed as usual. In this process, we query the Organization Username and Password to authenticate the users.

Prerequisites:

Created a record type in Account as a Person account. If your Salesforce organization enables the Person account feature, then you can skip the above step. The Remote Site settings URL must be a Domain of Login (Test or Login). Override the Standard functionality of a button in the Account (View, Tab) to show the below Visualforce page.

Apex Code Implementation: 

Apex Class: 

public class PasswordAuthentication_AC {

    Private Account accountInstance; 

    public id setId{get;set;} 

    public List<Account> accountInsList{get;set;} 

     

    //Other domain like test.salesforce.com,login.salesforce.com. 

    public final String domainName = 'login';  

     

    public String username { get { return UserInfo.getUsername(); } } 

    public transient String password {get;set;} 

     

    //Shouldn't be transmitted as part of the view state for a visualforce page. 

    Public Boolean personAccountFlag{get;set;} 

     

    Public Boolean defaultFlag{get;set;} 

     

    // Constructor to fetch current process record id and check account type as Person account. 

    public PasswordAuthentication_AC(Apexpages.StandardController aspc) 

    { 

        accountInstance=(Account)aspc.getrecord(); 

        personAccountFlag=false; 

        defaultFlag=true; 

        List<Account > personAcccountList=[select name from Account where Id =: accountInstance.id AND RecordTypeId in (SELECT Id FROM RecordType WHERE Name != 'PersonAccount')]; 

        if( accountInstance.id==null) 

        { 

            personAccountFlag=true; 

        } 

        else 

        { 

            if(personAcccountList.size()==0 ) 

            { 

                personAccountFlag=true; 

            } 

            else 

            { 

                personAccountFlag=False; 

            } 

        } 

         

    } 

     

    // ShowList to fetch the non person accounts from account list. 

    public void ShowList() 

    {    

        defaultFlag=false; 

        accountInsList=[select name,Phone from Account Where RecordTypeId in (SELECT Id FROM RecordType WHERE Name != 'PersonAccount')]; 

    } 

     

    // Authenticate method to create REST API callout. 

    public PageReference Authenticate() 

    { 

        try  

        { 

            HttpRequest request = new HttpRequest(); 

            request.setEndpoint('https://'+ domainName +'.salesforce.com/services/Soap/u/22.0'); 

            request.setMethod('POST'); 

            request.setHeader('Content-Type', 'text/xml;charset=UTF-8'); 

            request.setHeader('SOAPAction', '""'); 

            System.debug('Request  '+request); 

            request.setBody(buildSoapLogin(username,password)); 

            final Boolean verified = (new Http()).send(request).getBodyDocument().getRootElement() 

                .getChildElement('Body','http://schemas.xmlsoap.org/soap/envelope/') 

                .getChildElement('loginResponse','urn:partner.soap.sforce.com')!= null; 

            System.debug('verified     '+verified); 

            if(verified)  

            { 

                if(accountInstance.Id!=null) 

                { 

                    personAccountFlag=false; 

                    return Null; 

                } 

                else 

                { 

                    Schema.DescribeSObjectResult result = Account.SObjectType.getDescribe();  

                    PageReference pageRef = new PageReference('/' + result.getKeyPrefix());  

                    pageRef.setRedirect(true);  

                    return pageRef; 

                } 

            } 

            else   

            { 

                ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Incorrect password! You Are Not Authenticate user To handle this Confidential Records')); 

                Profile sys_adm_profile = [SELECT Id FROM Profile WHERE Name = 'System Administrator']; 

                User sys_adm = [SELECT id, Email FROM User WHERE ProfileId = :sys_adm_profile.Id]; 

                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); 

                mail.plainTextBody = 'Someone tried to login into your Account '; 

                mail.subject='Unauthorized user'; 

                mail.toAddresses = new String[] {sys_adm.Email}; 

                    Messaging.SingleEmailMessage[] messages =new List<Messaging.SingleEmailMessage> {mail}; 

                        Messaging.sendEmail(messages); 

                 

            } 

            return null; 

        } 

        catch (Exception e)  

        { 

            System.debug('Exception Issue'+e); 

            return null; 

        } 

    } 

     

    // buildSoapLogin method passes the username and password from REST API callout. 

    public static String buildSoapLogin(String username, String password) 

    { 

        try  

        { 

            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 ='<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Body>'+ w.getXmlString()+'</Body></Envelope>'; 

            System.debug('XML O/p   '+xmlOutput); 

            w.close(); 

            return xmlOutput; 

        } 

        catch (Exception e)  

        { 

            System.debug('Exception Issue'+e); 

            return null; 

        } 

    } 

     

    // Value method to redirect the current account Record. 

    public PageReference Value() 

    { 

        PageReference pageRef = new PageReference('/'+setId);  

        pageRef.setRedirect(true);  

        return pageRef; 

    } 

}

 

Visualforce Page 

<apex:page standardController="Account" extensions="PasswordAuthentication_AC" > 

    <apex:pageMessages /> 

    <apex:form rendered="{!personAccountFlag==true}"> 

        <apex:pageBlock title="Authenticate a Valid User" mode="view" rendered="{!defaultFlag==true}"> 

            <apex:pageBlockButtons location="bottom" > 

                <apex:commandButton value="Verify" action="{!Authenticate}"/> 

                <apex:commandLink title="Bussiness Accounts Only" value="Bussiness Accounts Only" action="{!ShowList}" ></apex:commandLink> 

            </apex:pageBlockButtons> 

            <apex:pageBlockSection title="Authenticate Section" columns="1"> 

                <apex:pageBlockSectionItem > 

                    <apex:outputLabel value="Username" for="username"/> 

                    <apex:outputText value="{!username}" id="username"/> 

                </apex:pageBlockSectionItem> 

                <apex:pageBlockSectionItem > 

                    <apex:outputLabel value="Password" for="password"/> 

                    <apex:inputSecret value="{!password}" id="password"/> 

                </apex:pageBlockSectionItem> 

            </apex:pageBlockSection> 

        </apex:pageBlock> 

        <apex:pageBlock rendered="{!defaultFlag=false}" title="Bussiness Accounts" tabStyle="Account"> 

            <apex:pageblocksection columns="1"> 

                <apex:pageBlockTable value="{!accountInsList}" var="accountList" > 

                    <apex:column ><apex:facet name="header">Name</apex:facet><apex:commandLink value=" {!accountList.Name}" action="{!value}" > 

                        <apex:param name="id" assignTo="{!setId}" value="{!accountList.id}"/> 

                        </apex:commandLink> </apex:column>   

                    <apex:column value="{!accountList.Phone}" /> 

                </apex:pageBlockTable> 

            </apex:pageblocksection> 

        </apex:pageBlock> 

    </apex:form> 

    <apex:form rendered="{!personAccountFlag==false}"> 

        <apex:detail subject="{!account.Id}" relatedList="true" title="true" inlineEdit="false" /> 

    </apex:form> 

    <script type = "text/javascript" > 

    function preventBack(){window.history.forward();} 

    setTimeout("preventBack()",0); 

    window.onunload=function(){return null}; 

    </script> 

</apex:page>

 

Sample Screen Shots:

Tab validation: 

Advanced Search Validation: 

Recent Items Validation: 

Invalid User validation: 

An Alert mail will be sent to Administrator of the respective Salesforce Organization

Business Accounts: 

Reference Link: API Callouts