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

9 comments:

  1. You said this is for Forms Based Authentication, what about Windows Authentication?

    ReplyDelete
  2. Hey,

    Thank you for sharing the post.

    The code has been executed successfully, but the thing is I have not received any thing in cookies.

    Kind Regards,
    D

    ReplyDelete
    Replies
    1. Did the authentication request/post work?
      Did you debug the part where you get a response from SharePoint. The response should contain cookies. Did you try logging onto SharePoint using a regular (windows pc) browser? Inspect that response to see if cookies are present.

      Delete
  3. can you upload the source code.

    ReplyDelete
  4. Thank you for this. For some reason, I am getting a 401 error when I use the code... Is there anything in addition that I need to do? I can send you my code if you like.

    ReplyDelete
    Replies
    1. Hi Andrew,
      On what url did it give a 404? I assume the /_vti_bin/authentication.asmx?
      Did you try to use fiddler to inspect the calls?
      I did nothing special to make this possible. Only a SP2013 with forms based authentication.
      401 means Unauthorized. So you could try to enable anonymous access on your web application (for testing). Let me know if that works.

      Delete
    2. Thank you for the quick response! Yes, it is when I try to access /_vti_bin/authentication.asmx on my client's SharePoint server... It is SP 2013 and I can authenticate ok with the iOS app I wrote in Objective-C using this library https://github.com/jimmywim/SPMobile.

      I'll try again and use Fiddler to see what is going on.

      Delete
  5. Hy Sir,

    I also tried your Code to authenticate in SharePoint.
    But the Server reponsed with a faultstring. userName cannot be null?
    Do you have an idea what i did wrong?

    Greetings

    ReplyDelete
    Replies
    1. Looks like SharePoint wants to receive userName. However in this example we send username as a parameter. It may have changed over time. You could try changing the parameter name. You could also try to call the authenticate.asmx an see if it generates a wsdl or something. Maybe that should what parameter you should sent.

      Delete

Note: Only a member of this blog may post a comment.