Monday 17 July 2017

Salesforce Rest API Username and Password authentication with community user

1. Copy and paste below code in your org


@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 responseBody = RestContext.request.requestBody.toString();                
//ValidateCredentials validate = (ValidateCredentials) System.JSON.deserialize(responseBody, ValidateCredentials.class);  

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;   
}

}


2. Create community and activate 

3. After community creation go to site> communityname > public access setting > Enabled Apex Class Access> Add above class"CommuntyLogin" and save

4. Modifiy below URL with your org deatils ,open new tab in browser and copy paste below URL 


Org with Without Name Space


https://imranshaik-******Community Domain Name********com/***Path**/services/apexrest/CommuntyLogin?username=skimran1038@gmail.com&password=1q********5t&orgId=00D******0I8RZ&domain=login


User Name : community user or normal user name

Password : Password

Org ID : Organization ID

domain : Production "login" , Sandbox "test
"

Ex: 

https://imranshaik-developer-edition.ap2.force.com/PopularTech/services/apexrest/CommuntyLogin?username=skimran1038@gmail.com&password=1********5t&orgId=00D2****I8RZ&domain=login


Org with Name Space 


https://imranshaik-******Community Domain Name********com/***Path**/services/apexrest/*****NameSpcae***/CommuntyLogin?username=skimran1038@gmail.com&password=1q***********5t&orgId=00D280*****Z&domain=login
             

NameSpcae : organization name space Ex: "ExpenceTracker"

User Name : community user or normal user name

Password : user Password

Org ID : Organization ID

domain : For Production "login" , Sandbox "test"


Ex: 

https://imranshaik-developer-edition.ap2.force.com/PopularTech/services/apexrest/ExpenceTracker/CommuntyLogin?username=skimran1038@gmail.com&password=1q2**********5t&orgId=00D280*********I8RZ&domain=login