3

Before I used Simple.OData.Client in our Xamarin project, I tried it with help of LINQPad. I was very impressed by the easiness to use it. When I built it into our Xamarin project I got exception by trying to get data from SharePoint's REST Api.

Simple.OData.Client.WebRequestException: Unexpected WebException encountered ---> System.Net.WebException: Error: SendFailure (Error writing headers) ---> System.Net.WebException: Error writing headers ---> System.IO.IOException: The authentication or decr...{Simple.OData.Client.WebRequestException: Unexpected WebException encountered ---> System.Net.WebException: Error: SendFailure (Error writing headers) ---> System.Net.WebException: Error writing headers ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a

As I believe, this exception was caused by the fact that our SharePoint instance using self-signed certificate. I tried it to eliminate by returning always true to the ServerCertificateValidationCallback

System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;

Now I get all the time Unauthorized exception from the Simple.OData.Client

Simple.OData.Client.WebRequestException: Unauthorized

Initial call is from MainViewModel through the business layer

    private async void InitializeAsync()
    {
        // TODO [Anton Kalcik - Dienstag, 05. Mai 2015 17:09:55]: Show loading indicator
        TaskEntity getTaskForCurrentMonthAsyncTask = await _taksBusinessLayer.GetTaskForCurrentMonthAsync();
        _timeToDisplay = getTaskForCurrentMonthAsyncTask.DueDate - DateTime.Now;
        // TODO [Anton Kalcik - Dienstag, 05. Mai 2015 17:10:04]: Hide loading indicator

        StartCountdownTimer();
    }

The class which executing the call is the class SharePointTaskRepository

public class SharePointTaskRepository : ITaskRepository
{
    private readonly string _collectionName;
    private readonly ODataClient _oDataClient;

    public SharePointTaskRepository(Uri sharepointUri, string collectionName, ICredentials credentials)
    {
        if (sharepointUri == null)
        {
            throw new ArgumentNullException("sharepointUri");
        }
        if (String.IsNullOrWhiteSpace(collectionName))
        {
            throw new ArgumentException("Argument can't be null, empty or white space!", "collectionName");
        }
        if (credentials == null)
        {
            throw new ArgumentNullException("credentials");
        }

        _collectionName = collectionName;

        var oDataClientSettings = new ODataClientSettings(sharepointUri, credentials);
        _oDataClient = new ODataClient(oDataClientSettings);
    }

    public async Task<IEnumerable<TaskModel>> ReadAsync(Expression<Func<TaskModel, bool>> filter, Expression<Func<TaskModel, object>> orderBy, int numberOfResults)
    {
        return await _oDataClient
                    .For<TaskModel>(_collectionName)
                    .Filter(filter)
                    .OrderBy(orderBy)
                    .Top(numberOfResults)
                    .FindEntriesAsync();
    }
}

I double checked the credentials, which are definitely correct. The code which utilize ServerCertificateValidationCallback is in the ApplicationRuntimeSettings. This class is platform specific, singleton and provided as all other objects by dependency injection.

[assembly: Dependency(typeof(ApplicationRuntimeSettings))]
namespace AZeitReminder.Droid.Infrastructure
{
    public class ApplicationRuntimeSettings : ApplicationRuntimeSettingsBase
    {
        public ApplicationRuntimeSettings()
        {
            System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
        }

        public override SQLiteConnection CreateSqLiteConnection()
        {
                string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                var path = Path.Combine(documentsPath, DatabaseFileName);
                var currentPlatform = new SQLitePlatformAndroid();
                var connection = new SQLiteConnection(currentPlatform, path);

                return connection;
        }

        public override CultureInfo GetCultureInfo()
        {
            var androidLocale = Java.Util.Locale.Default;
            var netLanguage = androidLocale.ToString().Replace("_", "-"); // NOTE [Anton Kalcik - Dienstag, 05. Mai 2015 17:21:10]: turns pt_BR into pt-BR
            return new CultureInfo(netLanguage);
        }
    }
}
Anton Kalcik
  • 2,107
  • 1
  • 25
  • 43

1 Answers1

1

You can try to set PreAuthenticate = false; for your request. Simple.ODataClient uses HttpClientHandler internally. This HttpClientHandler sets PreAuthenticate = true; but you can modify this handler in the OnApplyClientHandler and set the property to false. Try this in your code:

oDataClientSettings.OnApplyClientHandler = handler => handler.PreAuthenticate = false;

The reason is that your Sharepoint server can throw "Unauthorized" as a challenge response and the WebRequest would not answer to the challenge unless this property is false.