Monday, November 2, 2015

How to authenticate your Android App to SharePoint 2013

This blog post shows how to authenticate against an on premise SharePoint 2013 environment and do a simple REST call to retrieve the title of the root web. The SharePoint environment is configured to authenticate with Forms Based Authentication and runs on https.

Various other posts about an Android app authenticating to SharePoint are using a WebView solution. But I didn't want the user to use a integrated web view, but just do the authentication in code. The main point is to get hold of the FedAuth token from a cookie and pass the token back into all calls to SharePoint.

The main steps to authenticate and do a REST call to SharePoint are as follows:
1- Configure the CookieManager
2- Authenticate user: Start a AsyncTask to do a SOAP request to https://sharepoint.dev/_vti_bin/authentication.asmx
3- The cookie returned in step 2 will contain the FedAuth token.
4- the REST call: Start a HttpURLConnection to do a GET request to https://sharepoint.dev/_api/web/title
     Make sure this HttpURLConnection contains the FedAuth token in Cookie.
5- Parse JSON result

Now some more details please…

1- Configure the CookieManager
In Frament: onCreateView. Create a CookieManager that handles cookies within the VM. Make sure to set it to default. The network calls will use this CookieManager. That includes the CookieStore which will contain the cookies received by network calls.

   1: CookieHandler cookieHandler = CookieHandler.getDefault();
   2: if (cookieHandler==null) {
   3:     CookieManager cookieManager = new CookieManager();
   4:     CookieHandler.setDefault(cookieManager);
   5: }


2- Authenticate user
Doing a SOAP call to  https://sharepoint.dev/_vti_bin/authentication.asmx in AsyncTask, doInBackground.

Line 1-4: various SOAP settings
Line 6/7, 12/13: username and password
Line 24: doing the SOAP call

   1: String SOAP_ACTION1 = "http://schemas.microsoft.com/sharepoint/soap/Login";
   2: String NAMESPACE = "http://schemas.microsoft.com/sharepoint/soap/";
   3: String METHOD_NAME1 = "Login";
   4: String AuthURL = "https://sharepoint.dev/_vti_bin/authentication.asmx";
   5:  
   6: String username = "koenvanderlinden";
   7: String password = "P@ssw0rd";
   8:  
   9: SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME1);
  10:  
  11: //insert username and password to SoapEnvelope
  12: request.addProperty("username", username);
  13: request.addProperty("password", password);
  14:  
  15: //Declare the version of the SOAP request
  16: SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
  17:  
  18: envelope.setOutputSoapObject(request);
  19: envelope.dotNet = true;
  20:  
  21: try {
  22:     HttpTransportSE androidHttpTransport = new HttpTransportSE(AuthURL);
  23:  
  24:     // do the SoapCall
  25:     androidHttpTransport.call(SOAP_ACTION1, envelope, null);
  26:     // At this point the CookieManager will containt a cookie that has the FedAuth token.
  27:     
  28: } catch (Exception e) {
  29:     Log.v(LOG_TAG, e.toString());
  30:     e.printStackTrace();
  31: }

3- Inspect cookies
After the call androidHttpTransport.call() (line 22) the CookieManager will contain the cookies, including the FedAuth token.
To inspect the cookies add the following lines:

   1: // check cookies
   2: CookieManager cookieManager = (CookieManager)CookieHandler.getDefault();
   3: List<HttpCookie> cookies = cookieManager.getCookieStore().getCookies();

 
4- the REST call:

Line 4: Set REST url to get web title
Line 7: Create HttpUrlConnection
Line 10: Add application/json;odata=verbose to “Accept” header to get response in JSON format.
Line 11: Start the HttpURLConnection to do a GET request

The HttpURLConnection will use the CookieManager that is set as default. The CookieManager contains the CookieStore, which contains all cookies. Because the HttpURLConnection is doing a request to domain sharepoint.dev, the cookies will be added to the request that match on that domain. In this case the FedAuth cookie matches and is added to the request.

   1: HttpURLConnection urlConnection;
   2: BufferedReader reader;
   3:  
   4: Uri uri = Uri.parse("https://sharepoint.dev/_api/web/title");
   5: URL url = new URL(uri.toString());
   6:  
   7: urlConnection = (HttpURLConnection) url.openConnection();
   8: urlConnection.setRequestMethod("GET");
   9: // we need JSON formatted result
  10: urlConnection.setRequestProperty("Accept", "application/json;odata=verbose");
  11: urlConnection.connect();
  12:  
  13: // Read the input stream into a String
  14: InputStream inputStream = urlConnection.getInputStream();
  15: StringBuffer buffer = new StringBuffer();
  16: if (inputStream == null) {
  17:     // Nothing to do.
  18:     return null;
  19: }
  20: reader = new BufferedReader(new InputStreamReader(inputStream));
  21:  
  22: String line;
  23: while ((line = reader.readLine()) != null) {
  24:     // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
  25:     // But it does make debugging a *lot* easier if you print out the completed
  26:     // buffer for debugging.
  27:     buffer.append(line + "\n");
  28: }
  29:  
  30: if (buffer.length() == 0) {
  31:     // Stream was empty.  No point in parsing.
  32:     return null;
  33: }
  34:  
  35: // JSON result
  36: String restResult = buffer.toString();

5- Parse JSON result

   1: // parse JSON result
   2: JSONObject jsonResult = new JSONObject(restResultString).getJSONObject("d");
   3: title = jsonResult.getString("Title");

Additional Information
This code makes use of the ksoap2-android library. The Gradle configuratoin has been changes to include ksoap2 library by adding the following parts:

Add: repositories to android.buildTypes
        repositories {
            maven { url 'https://oss.sonatype.org/content/repositories/ksoap2-android-releases/' }
        }
Add: compile 'com.google.code.ksoap2-android:ksoap2-android:3.1.1' to dependencies.

Sample gradle:

   1: apply plugin: 'com.android.application'
   2:  
   3: android {
   4:     compileSdkVersion 23
   5:     buildToolsVersion "23.0.1"
   6:  
   7:     defaultConfig {
   8:         applicationId "nl.idoconsultancy.androidauthsp"
   9:         minSdkVersion 21
  10:         targetSdkVersion 22
  11:         versionCode 1
  12:         versionName "1.0"
  13:     }
  14:     buildTypes {
  15:         release {
  16:             minifyEnabled false
  17:             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  18:         }
  19:         repositories {
  20:             maven { url 'https://oss.sonatype.org/content/repositories/ksoap2-android-releases/' }
  21:         }
  22:     }
  23: }
  24:  
  25: dependencies {
  26:     compile fileTree(dir: 'libs', include: ['*.jar'])
  27:     compile 'com.android.support:appcompat-v7:23.0.1'
  28:     compile 'com.android.support:design:23.0.1'
  29:     compile 'com.google.code.ksoap2-android:ksoap2-android:3.1.1'
  30: }