Connecting Android apps to Dynamics CRM using SOAP endpoint

In our previous post, we covered how to connect iOS apps with Dynamics CRM using SOAP endpoint. In this blog post, we will look at how to connect android apps with Dynamics CRM using SOAP endpoint.

Application

The app takes Dynamics CRM online URL, username and password and returns the full name of the logged in user. The final application looks like below

 

Screenshot_2016-01-04-13-42-33     Screenshot_2016-01-04-13-51-32

 

Note: The complete code of the application is hosted on github.

Working

CRMAuth

This class creates and returns CRM Authentication SOAP Envelope and SOAP Header. CRM Authentication SOAP Envelope is used to authenticate user.


public class CRMAuth {

 public String GetAuthEnvelopeOnline(String url, String username, String password){

 String urnAddress = GetUrnOnline(url);
 Date now = new Date();

 StringBuilder xml = new StringBuilder();
 xml.append("<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\" xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">");
 xml.append("<s:Header>");
 xml.append("<a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>");
 xml.append("<a:MessageID>urn:uuid:" + java.util.UUID.randomUUID()
 + "</a:MessageID>");
 xml.append("<a:ReplyTo>");
 xml.append("<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>");
 xml.append("</a:ReplyTo>");
 xml.append("<a:To s:mustUnderstand=\"1\">https://login.microsoftonline.com/RST2.srf</a:To>");
 xml.append("<o:Security s:mustUnderstand=\"1\" xmlns:o=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">");
 xml.append("<u:Timestamp u:Id=\"_0\">");
 xml.append("<u:Created>" + String.format("%tFT%<tT.%<tLZ", now)
 + "</u:Created>");
 xml.append("<u:Expires>"
 + String.format("%tFT%<tT.%<tLZ", AddMinutes(60, now))
 + "</u:Expires>");
 xml.append("</u:Timestamp>");
 xml.append("<o:UsernameToken u:Id=\"uuid-"
 + java.util.UUID.randomUUID() + "-1\">");
 xml.append("<o:Username>" + username + "</o:Username>");
 xml.append("<o:Password>" + password + "</o:Password>");
 xml.append("</o:UsernameToken>");
 xml.append("</o:Security>");
 xml.append("</s:Header>");
 xml.append("<s:Body>");
 xml.append("<trust:RequestSecurityToken xmlns:trust=\"http://schemas.xmlsoap.org/ws/2005/02/trust\">");
 xml.append("<wsp:AppliesTo xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\">");
 xml.append("<a:EndpointReference>");
 xml.append("<a:Address>urn:" + urnAddress + "</a:Address>");
 xml.append("</a:EndpointReference>");
 xml.append("</wsp:AppliesTo>");
 xml.append("<trust:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</trust:RequestType>");
 xml.append("</trust:RequestSecurityToken>");
 xml.append("</s:Body>");
 xml.append("</s:Envelope>");

 return xml.toString();
 }

 public String createSOAPHeader(String url,String keyIdentifier, String token1, String token2){

 StringBuilder xml = new StringBuilder();
 xml.append("<s:Header>");
 xml.append("<a:Action s:mustUnderstand=\"1\">http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute</a:Action>");
 xml.append("<Security xmlns=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">");
 xml.append("<EncryptedData Id=\"Assertion0\" Type=\"http://www.w3.org/2001/04/xmlenc#Element\" xmlns=\"http://www.w3.org/2001/04/xmlenc#\">");
 xml.append("<EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#tripledes-cbc\"/>");
 xml.append("<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">");
 xml.append("<EncryptedKey>");
 xml.append("<EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p\"/>");
 xml.append("<ds:KeyInfo Id=\"keyinfo\">");
 xml.append("<wsse:SecurityTokenReference xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">");
 xml.append("<wsse:KeyIdentifier EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\" ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier\">"
 + keyIdentifier + "</wsse:KeyIdentifier>");
 xml.append("</wsse:SecurityTokenReference>");
 xml.append("</ds:KeyInfo>");
 xml.append("<CipherData>");
 xml.append("<CipherValue>" + token1 + "</CipherValue>");
 xml.append("</CipherData>");
 xml.append("</EncryptedKey>");
 xml.append("</ds:KeyInfo>");
 xml.append("<CipherData>");
 xml.append("<CipherValue>" + token2 + "</CipherValue>");
 xml.append("</CipherData>");
 xml.append("</EncryptedData>");
 xml.append("</Security>");
 xml.append("<a:MessageID>urn:uuid:" + java.util.UUID.randomUUID()
 + "</a:MessageID>");
 xml.append("<a:ReplyTo>");
 xml.append("<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>");
 xml.append("</a:ReplyTo>");
 xml.append("<a:To s:mustUnderstand=\"1\">" + url
 + "/XRMServices/2011/Organization.svc</a:To>");
 xml.append("</s:Header>");

 return xml.toString();
 }

 private String GetUrnOnline(String url) {
 if (url.toUpperCase().contains("CRM2.DYNAMICS.COM"))
 return "crmsam:dynamics.com";
 if (url.toUpperCase().contains("CRM4.DYNAMICS.COM"))
 return "crmemea:dynamics.com";
 if (url.toUpperCase().contains("CRM5.DYNAMICS.COM"))
 return "crmapac:dynamics.com";
 if (url.toUpperCase().contains("CRM6.DYNAMICS.COM"))
 return "crmoce:dynamics.com";
 if (url.toUpperCase().contains("CRM7.DYNAMICS.COM"))
 return "crmjpn:dynamics.com";
 if (url.toUpperCase().contains("CRM8.DYNAMICS.COM"))
 return "crmgcc:dynamics.com";

 return "crmna:dynamics.com";
 }

 private Date AddMinutes(int minutes, Date time) {
 long ONE_MINUTE_IN_MILLIS = 60000;
 long currentTime = time.getTime();
 Date newDate = new Date(currentTime + (minutes * ONE_MINUTE_IN_MILLIS));
 return newDate;
 }
}

 

CRMAuthHeader

This is a container class for CRMAuth returned values.


public class CRMAuthHeader {

private String header;

private Date Expire;

public CRMAuthHeader() {
 }

public CRMAuthHeader(String header, Date expire) {
 this.header = header;
 Expire = expire;
 }

public String getHeader() {
 return header;
 }

public void setHeader(String header) {
 this.header = header;
 }

public Date getExpire() {
 return Expire;
 }

public void setExpire(Date expire) {
 Expire = expire;
 }
}

CRMAuthenticationTask

This class executes SOAP Envelope from CRMAuth, takes organization URL, username and password and returns CRMAuthHeader object with following values:

  1. SOAP Header
  2. Expire Time

First is used to authenticate user as it is sent with every SOAP Request and Expire time is used to determine when the authentication token will expire.


public class CRMAuthenticationTask extends AsyncTask<String, Void, CRMAuthHeader> {

 public interface CRMAuthResponse {
 void processFinish(CRMAuthHeader output);

 void credentialError(String error);
 }

 Context context;
 CRMAuthResponse delegate = null;

 public CRMAuthenticationTask(CRMAuthResponse delegate,Context context) {
 this.delegate = delegate;
 this.context = context;
 }

 @Override
 protected CRMAuthHeader doInBackground(String... params) {
 if (params.length != 0 && !params[0].contentEquals("") && !params[1].contentEquals("") && !params[2].contentEquals("")) {
 ServiceHandler serviceHandler = new ServiceHandler();
 return serviceHandler.getAuthHeader(params[0], params[1], params[2]);
 } else {
 return null;
 }
 }

 @Override
 protected void onPostExecute(CRMAuthHeader crmAuthHeader) {
 if (crmAuthHeader != null) {
 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
 prefs.edit().putString("soap_header", crmAuthHeader.getHeader()).apply();
 prefs.edit().putString("expire", crmAuthHeader.getExpire().toString()).apply();
 delegate.processFinish(crmAuthHeader);
 } else {
 delegate.credentialError("Please provide Credentials");
 }
 }
}

WhoAmITask

This class contains and executes WhoAmIRequest and returns logged in UserId.


public class WhoAmITask extends AsyncTask<Void,Void,String> {

public interface WhoAmIResponse{
void whoAmIProcessFinish(String id);
}

WhoAmIResponse delegate = null;
LocalStorage localStorage;

public WhoAmITask(WhoAmIResponse delegate, Context context) {
this.delegate = delegate;
this.localStorage = new LocalStorage(context);
}

@Override
protected String doInBackground(Void... params) {
ServiceHandler handler = new ServiceHandler();
return handler.getUserId(localStorage.getOrganizationURL(), localStorage.getCRMAuthHeader(),WhoAmIBody());
}

@Override
protected void onPostExecute(String s) {
delegate.whoAmIProcessFinish(s);
}

private String WhoAmIBody() {
 StringBuilder xml = new StringBuilder();
 xml.append("<s:Body>");
 xml.append("<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\">");
 xml.append("<request i:type=\"c:WhoAmIRequest\" xmlns:b=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:c=\"http://schemas.microsoft.com/crm/2011/Contracts\">");
 xml.append("<b:Parameters xmlns:d=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\"/>");
 xml.append("<b:RequestId i:nil=\"true\"/>");
 xml.append("<b:RequestName>WhoAmI</b:RequestName>");
 xml.append("</request>");
 xml.append("</Execute>");
 xml.append("</s:Body>");
 return xml.toString();
 }
}

UserInfoTask

This class takes logged in UserId and returns user’s full name.


public class UserInfoTask extends AsyncTask<String, Void, String> {

public interface UserInfoResponse {
void userInfoProcessFinish(String fullName);
}

UserInfoResponse delegate = null;
LocalStorage localStorage;

public UserInfoTask(UserInfoResponse delegate, Context context) {
this.delegate = delegate;
this.localStorage = new LocalStorage(context);
}

@Override
protected String doInBackground(String... params) {
ServiceHandler handler = new ServiceHandler();
return handler.getUserInfo(localStorage.getOrganizationURL(), localStorage.getCRMAuthHeader(), UserInfoBody(params[0]));
}

@Override
protected void onPostExecute(String s) {
delegate.userInfoProcessFinish(s);
}

StringBuilder xml = new StringBuilder();
 xml.append("<s:Body>");
 xml.append("<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">");
 xml.append("<request i:type=\"a:RetrieveRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">");
 xml.append("<a:Parameters xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">");
 xml.append("<a:KeyValuePairOfstringanyType>");
 xml.append("<b:key>Target</b:key>");
 xml.append("<b:value i:type=\"a:EntityReference\">");
 xml.append("<a:Id>" + id + "</a:Id>");
 xml.append("<a:LogicalName>systemuser</a:LogicalName>");
 xml.append("<a:Name i:nil=\"true\" />");
 xml.append("</b:value>");
 xml.append("</a:KeyValuePairOfstringanyType>");
 xml.append("<a:KeyValuePairOfstringanyType>");
 xml.append("<b:key>ColumnSet</b:key>");
 xml.append("<b:value i:type=\"a:ColumnSet\">");
 xml.append("<a:AllColumns>false</a:AllColumns>");
 xml.append("<a:Columns xmlns:c=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">");
 xml.append("<c:string>firstname</c:string>");
 xml.append("<c:string>lastname</c:string>");
 xml.append("</a:Columns>");
 xml.append("</b:value>");
 xml.append("</a:KeyValuePairOfstringanyType>");
 xml.append("</a:Parameters>");
 xml.append("<a:RequestId i:nil=\"true\" />");
 xml.append("<a:RequestName>Retrieve</a:RequestName>");
 xml.append("</request>");
 xml.append("</Execute>");
 xml.append("</s:Body>");
 return xml.toString();
}

Conclusion

The given sample is great starter app to perform Dynamics CRM authentication in android apps. It gives a good overview of how you can make connection with Dynamics CRM from external apps not developed in .NET. You can freely use this sample in your apps for authentication purposes.

 



Leave a Reply

Request a Free Demo
Enter Your Information below and we will get back to you within few hours