Monday, 17 July 2017

Salesforce Rest API Username and Password authentication with community user


  
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
@RestResource(urlMapping='/CommuntyLogin/*')
global class CommuntyLogin {
  
 /**
 * To use this rest service first the user must issue a call to the /login GET method. if the credentials are valid
 * for this org, that call will return a session ID
 **/
  
   @HttpGet
   global static void doGet(){
       //setup return data
       RestContext.response.addHeader('Content-Type', 'application/json');
       RestResponseWrapper thisResponse = new RestResponseWrapper(RestContext.request);
       try{      
           RestRequest req = RestContext.request;
           RestResponse res = RestContext.response;             
           String username = RestContext.request.params.get('username');
           String password = RestContext.request.params.get('password');
           String orgId = RestContext.request.params.get('orgId');
           String loginDomain = RestContext.request.params.get('domain');
 
           loginResult thisResult = validLogin(username,password,orgId,loginDomain);
 
           if(!thisResult.success) {
               throw new applicationException('Invalid Login');
           }
           else {
               thisResponse.responseData = thisResult;
           }                              
       }
       catch(exception e){
           thisResponse.success = false;
           thisResponse.message= e.getMessage();
       }
       //return the data to the client.
       RestContext.response.responseBody = formatResponse(thisResponse); 
   }
  
  
 /**
 * Checks the validity of a provided username and password for a given org ID at the given domain.
 * @param username the username of the person the check. Looks like name@domain.tld
 * @param password the password of the user name to check
 * @param orgId the ID of the organization to validate the credentials against.
 * @param loginDomain the login domain the use, such as 'login' (for a prod/dev instance) or 'test' (for a sandbox instance)
 * @return loginResult a simple object with a boolean success flag and a sessionId if the login is successful.
 **/
 public static loginResult validLogin(string username, string password, Id orgID, string loginDomain){
   loginResult thisResult = new loginResult();
   thisResult.success = false;     
 
   //-------------------------------------------------------
 
   string xmlData = '<?xml version="1.0" encoding="utf-8"?>';    
   xmlData += '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">'
   xmlData += '<soapenv:Header>'
   xmlData += '<urn:LoginScopeHeader>'
   xmlData += '<urn:organizationId>'+orgID+'</urn:organizationId>'
   xmlData += '</urn:LoginScopeHeader>'
   xmlData += '</soapenv:Header>'
   xmlData += '<soapenv:Body>';
   xmlData += '<urn:login>';
   xmlData += '<urn:username>'+username+'</urn:username>';
   xmlData += '<urn:password>'+password+'</urn:password>';
   xmlData += '</urn:login>';
   xmlData += '</soapenv:Body>'
   xmlData += '</soapenv:Envelope>';
 
   //------------------------------------------------------
 
   //create the HTTP request to send to the SOAP API
   HttpRequest request = new HttpRequest();
   request.setEndpoint('https://'+loginDomain+'.salesforce.com/services/Soap/c/39.0');
   request.setMethod('POST');
   request.setHeader('Content-Type', 'text/xml;charset=UTF-8');
   request.setHeader('SOAPAction', '""');
   request.setBody(xmlData);      
   HttpResponse response = new Http().send(request);
   
  final Boolean verified = response.getBodyDocument().getRootElement()
    .getChildElement('Body','http://schemas.xmlsoap.org/soap/envelope/')
    .getChildElement('loginResponse','urn:enterprise.soap.sforce.com') != null;
   
   
       //if the login is valid, now check to see if the orgId this account is for matches the given orgId
       if(verified){
           final string userOrgId = readXmlElement(string.valueOf(response.getBody()), 'organizationId');
           if(userOrgId == orgId){
               thisResult.sessionId = readXmlElement(string.valueOf(response.getBody()), 'sessionId');
               thisResult.success = true;
           }
       }
       return thisResult;
   }
  
   /**
   * Reads a single XML attribute element from an XML document
   * useful for just getting a single element from some XML without having to go through the complexity of
   * full on parsing.
   * @param xmlBody the XML document body to find elementName in
   * @elementName the XML element to read the value from and return.
   * @return the value of the element if it exists, or null if it does not.
   **/
   public static string readXmlElement(string xmlBody, string elementName){       
       Xmlstreamreader reader = new Xmlstreamreader(xmlBody);
       while(reader.hasNext()) {
           if (reader.getEventType() == XmlTag.START_ELEMENT &&  reader.getLocalName() == elementName){
               reader.next();
           return getDecodedString(reader);
           }
           reader.next();
       }    
       return null;
   }
  
 /**
 * Decodes a URL encoded string into a non encoded string
 **/
   public static String getDecodedString(Xmlstreamreader reader){
       return EncodingUtil.urlDecode(reader.getText(), 'UTF-8').trim();
   }
  
   /**   
   * take one of those wrapper objects and format it by wrapping it in a callback if needed, and serializing
   * the result into json. Callbacks allow for cross domain javascript requests. Uses callback param in the url
   * @param responseData an object of whatever data you want to return to the client. Can be anything that can be serialized as JSON
   * @return a blob to be sent to the client. JSON encoded, wrapped in callback if one is provided in the URL.
   **/  
   public static blob formatResponse(object responseData){
       string response = JSON.serialize(responseData);
       return blob.valueOf(response);
   }
  
   /**
   * Result of login call. More data could be populated here, but this is all we need for now
   **/
   public class loginResult{
       public boolean success{get;set;}
       public string sessionId{get;set;}
   }   
  
 /** Custom Exception Class **/
 public class applicationException extends Exception {}
 
   /**
   * simple wrapper response class. Makes it so all replies via this API have the same basic structure
   * including a boolean success flag, a message, any sObjects affected and some debugging params.
   **/
   public class restResponseWrapper{
       public string message;
       public boolean success;   
       public object responseData;
       public object inputData;
       private string requestURI;
 
       public restResponseWrapper(RestRequest reqContext){          
           message = 'run successful';
           success = true;
           requestURI = reqContext.requestURI;
       }
   }
 
   public class ValidateCredentials{
       public String username;
       public String password;
       public String orgId;   
       public String domain;  
   }
  
}
 

  

No comments:

Post a Comment