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:
IAuthService
in the Services
folder in the core library:public interface IAuthService { }
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.
Components
folder.AuthService
in the Services
folder in the TripLog.iOS project:public class AuthService : IAuthService { }
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); }; } }
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:
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; } }
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.
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; } // ... }
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); }
18.217.147.193