Creating an authentication service

Now that we have enabled our backend service with Facebook authentication, the app as it is from the previous chapter will fail to load content. In this section, we will update the app to authenticate users with Facebook via OAuth and obtain an access token from Azure that can be used in subsequent API calls by the TripLogApiDataService.

As in the previous chapter, instead of using the Azure Mobile Apps SDK, we will directly call the REST endpoints behind the SDK to better understand the approach to authenticate to an API in a more generic way. In order to do this, we will first make an OAuth call to Facebook, obtaining a Facebook token. We will then pass that token to an Azure Mobile App endpoint, where it is validated using the Facebook app ID and secret that was added to the service's configuration in Azure to finally receive the access token needed to make calls to the API table endpoints.

Performing OAuth in a mobile app requires a certain set of platform-specific capabilities; so, we will need to follow the same pattern that we did in earlier chapters, as follows:

  1. First, create a new interface named IAuthService in the Services folder in the core library:
    public interface IAuthService
    { }
  2. Update the IAuthService interface with a single method that takes in all the key components of a standard OAuth call as its parameters:
    public interface IAuthService
    {
        Task 
    SignInAsync (string clientId,
            Uri authUrl,
            Uri callbackUrl,
            Action<string> tokenCallback,
            Action<string> errorCallback);
    }

    The two callback Action parameters provide a way to handle both success and failure OAuth responses.

Next, we need to create an implementation of this interface. Just as we did with the geo-location service in Chapter 4, Platform Specific Services and Dependency Injection, we will leverage a Xamarin Component to create the actual platform-specific implementation for IAuthService. The Xamarin.Auth Component provides an easy to use cross-platform API to conduct OAuth in Xamarin mobile apps.

  1. Add the Xamarin.Auth Xamarin Component to each of the platform projects (that is, TripLog.iOS and TripLog.Droid) by right-clicking on the Components folder.
  2. Next, create a new class named AuthService in the Services folder in the TripLog.iOS project:
    public class AuthService : IAuthService
    { }
  3. Next, provide the implementation for the SignInAsync method:
    public class AuthService : IAuthService
    {
        public async Task SignInAsync (string clientId,
            Uri authUrl,
            Uri callbackUrl,
            Action<string> tokenCallback,
            Action<string> errorCallback)
        {
            var auth = new OAuth2Authenticator (clientId,
                string.Empty,
                authUrl,
                callbackUrl);
    
            auth.AllowCancel = true;
    
            var controller = auth.GetUI();
            await UIApplication.SharedApplication
                .KeyWindow
                .RootViewController
                .PresentViewControllerAsync(controller, true);
    
            auth.Completed += (s, e) =>
            {
                controller.DismissViewController(true, null);
                if (e.Account != null && e.IsAuthenticated)
                {
                    if (tokenCallback != null)
                        tokenCallback(e.Account
                            .Properties["access_token"]);
                }
                else
                {
                    if (errorCallback != null)
                        errorCallback("Not authenticated");
                }
            };
    
            auth.Error += (s, e) =>
            {
                controller.DismissViewController(true, null);
                if (errorCallback != null)
                    errorCallback(e.Message);
            };
        }
    }
  4. Finally, update the TripLogPlatformModule in each platform-specific project to register its IAuthService implementation into the IoC:
    public class TripLogPlatformModule : NinjectModule
    {
        public override void Load ()
        {
            Bind<ILocationService> ()
                .To<LocationService> ()
                .InSingletonScope ();
    
            Bind<IAuthService> ()
                .To<AuthService> ()
                .InSingletonScope ();
        }
    }

The IAuthService provides a way to perform OAuth against Facebook, which gives us a Facebook authentication token, but we still need a way to pass that Facebook specific token to our API to get back an Azure authenticated access token that we can use in our requests. Azure Mobile Apps provide an endpoint that takes an identity provider-specific token, and in return, provides back an Azure-specific token. In order to use this endpoint, we just need to update our TripLog data service with a new method, as follows:

  1. First, create a new model class named TripLogApiAuthToken. As we saw in the preceding section, the response from the /.auth/login/facebook endpoint is a JSON object containing a user object and an authenticationToken object; so, this TripLogApiAuthToken model will represent that structure so that we can deserialize the response and use the access token for future calls to the TripLog backend service:
    public class TripLogApiUser
    {
        public string UserId { get; set; }
    }
    public class TripLogApiAuthToken
    {
        public TripLogApiUser User { get; set; }
        public string AuthenticationToken { get; set; }
    }
  2. Next, add a new method to the ITripLogDataService interface named GetAuthTokenAsync that returns an object of the TripLogApiAuthToken type we just created:
    public interface ITripLogDataService
    {
        Task<TripLogApiAuthToken> GetAuthTokenAsync(string idProvider, string idProviderToken);
        Task<IList<TripLogEntry>> GetEntriesAsync ();
        Task<TripLogEntry> GetEntryAsync (string id);
        Task<TripLogEntry> AddEntryAsync (TripLogEntry entry);
        Task<TripLogEntry> UpdateEntryAsync (TripLogEntry entry);
        Task RemoveEntryAsync (TripLogEntry entry);
    }

    Notice the idProvider parameter, which allows this method to be used for Azure social identity providers beyond just Facebook.

  3. Next, update the TripLogApiDataService to include the implementation for the GetAuthTokenAsync method that we just added to ITripLogDataService. The method needs to make a POST call to the /.auth/login/facebook endpoint with the access token received from the OAuth response in the request body. The service endpoint expects the token in the body to be associated with a key named access_token. Because our base HTTP service handles serializing the message body data for us, we can simply create a struct to house the token that will be passed to the endpoint:
    public class TripLogApiDataService
       : BaseHttpService, ITripLogDataService
    {
        readonly Uri _baseUri;
        IDictionary<string, string> _headers;
    
        // ...
    
        struct IdProviderToken
        {
            [JsonProperty("access_token")]
            public string AccessToken { get; set; }
        }
    
        public async Task<TripLogApiAuthToken> GetAuthTokenAsync(string idProvider, string idProviderToken)
        {
            var token = new IdProviderToken {
                AccessToken = idProviderToken
            };
    
            var url = new Uri (_baseUri,
                string.Format (".auth/login/{0}", idProvider));
    
            var response = await SendRequestAsync<TripLogApiAuthToken> (url, HttpMethod.Post, _headers, token);
    
            // Update this service with the new auth token
            if (response != null) {
                var authToken = response.AuthenticationToken;
                _headers["x-zumo-auth"] = authToken;
            }
    
            return response;
        }
    
        // ...
    }
  4. Finally, we need to update the TripLogApiDataService constructor with a string parameter named authToken. In the GetAuthTokenAsync method, we update the _headers property within the service with token we received from the backend. However, we also need to be able to set the _headers property from the constructor so that we can initialize the service with a token, if one already exists (for instance, if a token was persisted in the app's settings after signing in).
    public TripLogApiDataService (Uri baseUri,
        string authToken)
    {
        _baseUri = baseUri;
        _headers = new Dictionary<string, string> ();
    
        _headers.Add ("zumo-api-version", "2.0.0");
        _headers.Add ("x-zumo-auth", authToken);
    }
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.147.193