Randeep Singh
Randeep Singh

Reputation: 1018

Azure Mobile App OAuth with API Controller

Using above service with Xamarin form, I have enabled authentication with OAuth (Microsoft and Google) at server level.

Call from Swagger works fine. However I'm getting 401 error accessing this via the app. This neither works for TableController nor APIController. I'm not using EasyTables. Following is my code.

public async Task<bool> AuthenticateAsync()
{
    bool success = false;
    try
    {
        if (user == null)
        {
            user = await ItemManager.DefaultManager.CurrentClient.LoginAsync(this, MobileServiceAuthenticationProvider.MicrosoftAccount);

            Constants.MobileToken = user.MobileServiceAuthenticationToken;
        }
        success = true;
    }
    catch (Exception ex)
    {
        CreateAndShowDialog(ex.Message, "Authentication failed");
    }
    return success;
}

public async Task<ObservableCollection<Item>> GetItemsAsync(bool syncItems = false)
    {
        try
        {
            IEnumerable<Item> items = await itemTable
                .ToEnumerableAsync();

            return new ObservableCollection<Item>(items);
        }
        catch (MobileServiceInvalidOperationException msioe)
        {
            Debug.WriteLine(@"Invalid sync operation: {0}", msioe.Message);
        }
        catch (Exception e)
        {
            Debug.WriteLine(@"Sync error: {0}", e.Message);
        }
        return null;
    }

I tried using rest service client, but not sure how to pass the authentication header. As I seen by Swagger, its actually sending via cookie AppServiceAuthSession. How should it be done via Xamarin Forms?

    public ItemManager(IRestService service)
    {
        restService = service;
    }

    public Task<List<Item>> GetTasksAsync()
    {
        return restService.RefreshDataAsync();
    }

I read that the token we must supply as the 'X-ZUMO-AUTH' is not the access token that provider send back to us; it is the token that the mobile service backend sends back. How we suppose to retrieve this token? And I don't see Swagger sending X-Zumo-Auth header.

Following is my Rest Service initialization :

    public RestService()
    {


        client = new HttpClient(new LoggingHandler(true));
        client.MaxResponseContentBufferSize = 256000;
        client.DefaultRequestHeaders.Add("x-access_type", "offline");
        client.DefaultRequestHeaders.Add("x-zumo-auth", Constants.MobileToken);
        client.DefaultRequestHeaders.Add("ZUMO-API-VERSION", "2.0.0");
    }

 public async Task<List<Item>> RefreshDataAsync()
    {
        Items = new List<Item>();
        var uri = new Uri(string.Format(Constants.RestUrl, string.Empty));
        try
        {
            var response = await client.GetAsync(uri);
            if (response.IsSuccessStatusCode)
            {
                var content = await response.Content.ReadAsStringAsync();
                Items = JsonConvert.DeserializeObject<List<Item>>(content);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(@"              ERROR {0}", ex.Message);
        }

        return Items;
    }

EDIT

After enabling the server logging - Azure service is actually throwing 404 error. And this only happens if I enable the custom authorization on the server.

After debugging the code, I notice following difference between authentication handled by both Mobile App vs Swagger : Mobile App sets the Authentication Type as Federation, but Swagger is setting it correctly as microsoftaccount Mobile App Swagger

And this makes the ID different as well :

Mobile App Swagger

I must not be passing the token correctly here.

Upvotes: 0

Views: 399

Answers (1)

Randeep Singh
Randeep Singh

Reputation: 1018

So what I figured out so far is that I need to pass the header X-ZUMO-AUTH with the current user token to make it work.

And handle this header in the API code to make retrieve user details

         //Try to retrieve from header if available
        actionContext.Request.Headers.TryGetValues("x-zumo-auth", out auth_token);

        if (auth_token !=null)
        {
            try
            {
                string urlPath = string.Concat(new Uri(actionContext.Request.RequestUri, actionContext.Request.GetRequestContext().VirtualPathRoot).AbsoluteUri, ".auth/me");
                var result = Get<List<AzureUserDetail>>(HttpWebRequest.Create(urlPath), auth_token.FirstOrDefault(), null)?.FirstOrDefault();
                userID = result.User_Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Val;
            }
            catch
            {
                actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.NotAcceptable);
            }
        }

Upvotes: 0

Related Questions