Introduction
This sample shows how to connect universal apps to Facebook, Google and Microsoft accounts using the MVVM pattern.
Building the Sample
You only need Visual Studio 2012 or Visual Studio 2013 and Windows 8 or Windows 8.1, both the RTM version.
Description
Recently published was Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM) and this sample has the same goal, but now the target is Universal Apps. Both have the goal to use the MVVM pattern.
Before starting this sample, analize to see if the SDKs used in the first sample could be used in this new sample and in a first attempt didn´t find a package for all the targets (Windows 8.1 and Windows Phone 8.1 Runtime). Let's see that analysis.
The packages used was:
- Facebook SDK for Windows Phone (http://facebooksdk.net/docs/phone/ )
- Google APIs Auth Client and Google APIs OAuth2 Client (https://www.nuget.org/packages/Google.Apis.Auth/ and https://www.nuget.org/packages/Google.Apis.Authentication/1.6.0-beta )
- Live SDK (http://msdn.microsoft.com/en-US/onedrive/dn630256 )
The packages that were analized are:
- Facebook SDK for Windows Phone: there is a package for Windows 8.1 Store Apps but there isn't for Windows Phone 8.1 Runtime (we cannot use the version from Windows Phone 8.0 because it uses a namespace for controls that does not exist in the Windows Phone 8.1 Runtime)
- Google APIs Auth Client and Google APIs OAuth2 Client: there is a package compatible with Windows 8.1 Store Apps but it is a bit different from the sample created before, the API changed. And there isn't a package for the Windows Phone 8.1 Runtime.
- Live SDK: is compatible with the Windows Phone 8.1 Runtime and Windows 8.1.
The next step, was to try to port the package for Google from Windows 8.1 Store Apps to Windows Phone 8.1 Runtime, create the logic because there is a lot of code shared between them and then the hard work began.
After some attempts, the code started to throw the exception NotImplementedException because the WebAuthenticationBroker class does not work the same way for these targets. There is a sample that shows this difference, here is the source code and we will see this in this sample.
In conclusion of this analysis, I decided to use WebAuthenticationBroker for authentication using Facebook and Google accounts and Live SDK for Microsoft accounts.
Let's start the sample!
Note: This sample uses MVVM Light and Cimbalino Toolkit.
For each provider it is necessary to get the app id/client id/client secrect in their websites.
For Google go to https://console.developers.google.com/project and create a new project (APIs and auth > credentials).
For Facebook go to https://developers.facebook.com/ and create a new app.
For Live SDK go to https://account.live.com/developers/applications/index and create one or use an existing app.
Before you start you should change the Constant file to add client ids / client secret / app id, without it the app fails!!
-
-
-
- public class Constants
- {
-
-
-
- #if !WINDOWS_PHONE_APP
- public const string GoogleCallbackUrl = "urn:ietf:wg:oauth:2.0:oob";
- #else
- public const string GoogleCallbackUrl = "http://localhost";
- #end
-
-
-
- public const string FacebookAppId = "<app id>";
-
-
-
-
- public const string GoogleClientId = "<client id>";
-
-
-
-
- public const string GoogleClientSecret = "<client secret";
-
-
-
-
- public const string LoginToken = "LoginToken";
-
-
-
-
- public const string FacebookProvider = "facebook";
-
-
-
-
- public const string GoogleProvider = "google";
-
-
-
-
- public const string MicrosoftProvider = "microsoft";
- }
This sample will use the same idea used in
Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM). There are classes that have the same goal and the same name. Like in that sample, this sample created a SessionService that manages the Login and Logout using a provider value. That is nice because in LoginView we set the buttons to the same command and for each command we set the provider in commandparameter. With it, the LoginView and LoginViewModel are more clear and simple.
The following are the classes created:
- FacebookService has all code related with authentication with Facebook account;
- MicrosoftService has all code related with authentication with Microsoft account;
- GoogleService has all code related with authentication with Google account;
- SessionService call the methods login or logout for the provide requested;
The Flow
To help to understood the difference in the flow in each platform some diagrams were created.
The flow for the Windows 8.1 Store apps will be:
The flow for Windows Phone 8.1 Runtime will be:
- using Microsoft account
- using Facebook Account or Google account
Note: Start in LoginView using the blue arrow and when the blue flow finnishes, go to FacebookService/GoogleService and follow the red flow.
Like we can see, the authentication for Windows Phone 8.1 is very complicated, it could be easier like in Windows 8.1 Store apps. And It breaks the MVVM Pattern!
This sample is a Universal App, for this reason the code can be found in the Shared Project and to add specific features for each target directives (#if #else #endif) are used, it can cause some difficulties for understanding the code but is a good way to have only one code in one place. Partial methods and classes can be used here, because in most cases directives are added to add a method for Windows Phone.
The FacebookService
-
-
-
- public class FacebookService : IFacebookService
- {
- private readonly ILogManager _logManager;
-
-
-
-
-
-
-
- public FacebookService(ILogManager logManager)
- {
- _logManager = logManager;
- }
-
-
-
-
-
-
-
- public async Task<Session> LoginAsync()
- {
- const string FacebookCallbackUrl = "https://m.facebook.com/connect/login_success.html";
- var facebookUrl = "https://www.facebook.com/dialog/oauth?client_id=" + Uri.EscapeDataString(Constants.FacebookAppId) + "&redirect_uri=" + Uri.EscapeDataString(FacebookCallbackUrl) + "&scope=public_profile,email&display=popup&response_type=token";
-
- var startUri = new Uri(facebookUrl);
- var endUri = new Uri(FacebookCallbackUrl);
-
- #if WINDOWS_PHONE_APP
- WebAuthenticationBroker.AuthenticateAndContinue(startUri, endUri, null, WebAuthenticationOptions.None);
- return null;
- #else
- var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, startUri, endUri);
- return GetSession(webAuthenticationResult);
- #endif
- }
-
- private void GetKeyValues(string webAuthResultResponseData, out string accessToken, out string expiresIn)
- {
- string responseData = webAuthResultResponseData.Substring(webAuthResultResponseData.IndexOf("access_token", StringComparison.Ordinal));
- string[] keyValPairs = responseData.Split('&');
- accessToken = null;
- expiresIn = null;
- for (int i = 0; i < keyValPairs.Length; i++)
- {
- string[] splits = keyValPairs[i].Split('=');
- switch (splits[0])
- {
- case "access_token":
- accessToken = splits[1];
- break;
- case "expires_in":
- expiresIn = splits[1];
- break;
- }
- }
- }
-
-
-
-
-
-
-
-
-
-
-
- private async Task<UserInfo> GetFacebookUserNameAsync(string accessToken)
- {
- var httpClient = new HttpClient();
- var response = await httpClient.GetStringAsync(new Uri("https://graph.facebook.com/me?access_token=" + accessToken));
- var value = JsonValue.Parse(response).GetObject();
- var facebookUserName = value.GetNamedString("name");
-
- return new UserInfo
- {
- Name = facebookUserName,
- };
- }
-
-
-
-
- public async void Logout()
- {
- Exception exception = null;
- try
- {
-
- }
- catch (Exception ex)
- {
- exception = ex;
- }
- if (exception != null)
- {
- await _logManager.LogAsync(exception);
- }
- }
-
- #if WINDOWS_PHONE_APP
- public async Task<Session> Finalize(WebAuthenticationBrokerContinuationEventArgs args)
- {
- Exception exception = null;
- try
- {
- var result = args.WebAuthenticationResult;
-
- return GetSession(result);
- }
- catch (Exception e)
- {
- exception = e;
- }
-
- await _logManager.LogAsync(exception);
-
- return null;
- }
- #endif
- private Session GetSession(WebAuthenticationResult result)
- {
- if (result.ResponseStatus == WebAuthenticationStatus.Success)
- {
- string accessToken;
- string expiresIn;
- GetKeyValues(result.ResponseData, out accessToken, out expiresIn);
-
- return new Session
- {
- AccessToken = accessToken,
- ExpireDate = new DateTime(long.Parse(expiresIn)),
- Provider = Constants.FacebookProvider
- };
- }
- if (result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
- {
- throw new Exception("Error http");
- }
- if (result.ResponseStatus == WebAuthenticationStatus.UserCancel)
- {
- throw new Exception("User Canceled.");
- }
- return null;
- }
- }
In this class, in LoginAsync, we could see the directives that define the code for each platform. The first attempt was a bit complicated to define a solution for it, using null was the solution (for Windows Phone, of course!). This class doesn´t do anything in the logout method, we could remove it, but to keep the same pattern in all the classes it wasn´t removed and the session is not saved in webview, at least it is possible to login using a different account when the LoginView is shown again.
The GoogleService
public
class
GoogleService : IGoogleService
{
private
readonly
ILogManager _logManager;
/// <summary>
/// Initializes a new instance of the <see cref="GoogleService"/> class.
/// </summary>
/// <param name="logManager">
/// The log manager.
/// </param>
public
GoogleService(ILogManager logManager)
{
_logManager = logManager;
}
/// <summary>
/// The login async.
/// </summary>
/// <returns>
/// The <see cref="Task"/> object.
/// </returns>
public
async Task<Session> LoginAsync()
{
var googleUrl =
new
StringBuilder();
googleUrl.Append(Uri.EscapeDataString(Constants.GoogleClientId));
googleUrl.Append(
"&scope=openid%20email%20profile"
);
googleUrl.Append(
"&redirect_uri="
);
googleUrl.Append(Uri.EscapeDataString(Constants.GoogleCallbackUrl));
googleUrl.Append(
"&state=foobar"
);
googleUrl.Append(
"&response_type=code"
);
var startUri =
new
Uri(googleUrl.ToString());
#if !WINDOWS_PHONE_APP
var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, startUri, endUri);
return
await GetSession(webAuthenticationResult);
#else
WebAuthenticationBroker.AuthenticateAndContinue(startUri,
new
Uri(Constants.GoogleCallbackUrl),
null
, WebAuthenticationOptions.None);
return
null
;
#endif
}
private
string
GetCode(
string
webAuthResultResponseData)
{
// Success code=4/izytpEU6PjuO5KKPNWSB4LK3FU1c
var split = webAuthResultResponseData.Split(
'&'
);
return
split.FirstOrDefault(value => value.Contains(
"code"
));
}
/// <summary>
/// The logout.
/// </summary>
public
void
Logout()
{
}
#if WINDOWS_PHONE_APP
public
async Task<Session> Finalize(WebAuthenticationBrokerContinuationEventArgs args)
{
Exception exception =
null
;
try
{
return
await GetSession(args.WebAuthenticationResult);
}
catch
(Exception e)
{
exception = e;
}
await _logManager.LogAsync(exception);
return
null
;
}
#endif
private
async Task<Session> GetSession(WebAuthenticationResult result)
{
if
(result.ResponseStatus == WebAuthenticationStatus.Success)
{
var code = GetCode(result.ResponseData);
var serviceRequest = await GetToken(code);
return
new
Session
{
AccessToken = serviceRequest.access_token,
ExpireDate =
new
DateTime(
long
.Parse(serviceRequest.expires_in)),
Id = serviceRequest.id_token,
Provider = Constants.GoogleProvider
};
}
if
(result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
throw
new
Exception(
"Error http"
);
}
if
(result.ResponseStatus == WebAuthenticationStatus.UserCancel)
{
throw
new
Exception(
"User Canceled."
);
}
return
null
;
}
private
static
async Task<ServiceResponse> GetToken(
string
code)
{
var body =
new
StringBuilder();
body.Append(code);
body.Append(
"&client_id="
);
body.Append(Uri.EscapeDataString(Constants.GoogleClientId));
body.Append(
"&client_secret="
);
body.Append(Uri.EscapeDataString(Constants.GoogleClientSecret));
body.Append(
"&redirect_uri="
);
body.Append(Uri.EscapeDataString(Constants.GoogleCallbackUrl));
body.Append(
"&grant_type=authorization_code"
);
var client =
new
HttpClient();
var request =
new
HttpRequestMessage(HttpMethod.Post,
new
Uri(TokenUrl))
{
Content =
new
StringContent(body.ToString(), Encoding.UTF8,
"application/x-www-form-urlencoded"
)
};
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
var serviceTequest = JsonConvert.DeserializeObject<ServiceResponse>(content);
return
serviceTequest;
}
}
Here, we don´t have anything new, this is similar to the FacebookService.
Note: FacebookService and GoogleService are similar but the response and request are different for the reason that these classes are not joined.
Attention:Google changed the authentication and this sample uses the last version. See more about it here .
The MicrosoftService
This class was reused from the
sample Authentication using Facebook, Google and Microsoft account in WP8.0 App (MVVM), but for it work is required to associate the app to the store.
That will result in an additional file in the project: as in the following:
Note
- These file are removed from the sample because each developer has their own.
- In devices that the Microsoft account is logged the application will do the login automatically.
The SessionService
-
-
-
- public class SessionService : ISessionService
- {
- private readonly IApplicationDataService _applicationSettings;
- private readonly IFacebookService _facebookService;
- private readonly IMicrosoftService _microsoftService;
- private readonly IGoogleService _googleService;
- private readonly ILogManager _logManager;
-
-
-
-
-
-
-
-
-
- public SessionService(IApplicationDataService applicationSettings,
- IFacebookService facebookService,
- IMicrosoftService microsoftService,
- IGoogleService googleService, ILogManager logManager)
- {
- _applicationSettings = applicationSettings;
- _facebookService = facebookService;
- _microsoftService = microsoftService;
- _googleService = googleService;
- _logManager = logManager;
- }
-
-
-
-
-
- public Session GetSession()
- {
- var expiryValue = DateTime.MinValue;
- string expiryTicks = LoadEncryptedSettingValue("session_expiredate");
- if (!string.IsNullOrWhiteSpace(expiryTicks))
- {
- long expiryTicksValue;
- if (long.TryParse(expiryTicks, out expiryTicksValue))
- {
- expiryValue = new DateTime(expiryTicksValue);
- }
- }
-
- var session = new Session
- {
- AccessToken = LoadEncryptedSettingValue("session_token"),
- Id = LoadEncryptedSettingValue("session_id"),
- ExpireDate = expiryValue,
- Provider = LoadEncryptedSettingValue("session_provider")
- };
- _applicationSettings.LocalSettings[Constants.LoginToken] = true;
- return session;
- }
-
-
-
-
-
-
-
- private void Save(Session session)
- {
- SaveEncryptedSettingValue("session_token", session.AccessToken);
- SaveEncryptedSettingValue("session_id", session.Id);
- SaveEncryptedSettingValue("session_expiredate", session.ExpireDate.Ticks.ToString(CultureInfo.InvariantCulture));
- SaveEncryptedSettingValue("session_provider", session.Provider);
- _applicationSettings.LocalSettings[Constants.LoginToken] = true;
- }
-
-
-
-
- private void CleanSession()
- {
- _applicationSettings.LocalSettings.Remove("session_token");
- _applicationSettings.LocalSettings.Remove("session_id");
- _applicationSettings.LocalSettings.Remove("session_expiredate");
- _applicationSettings.LocalSettings.Remove("session_provider");
- _applicationSettings.LocalSettings.Remove(Constants.LoginToken);
- }
-
-
-
-
-
-
-
-
-
-
- public async Task<bool?> LoginAsync(string provider)
- {
- Provider = provider;
- Exception exception;
- try
- {
- Session session = null;
- switch (provider)
- {
- case Constants.FacebookProvider:
- session = await _facebookService.LoginAsync();
- break;
- case Constants.MicrosoftProvider:
- session = await _microsoftService.LoginAsync();
- break;
- case Constants.GoogleProvider:
- session = await _googleService.LoginAsync();
- break;
- }
- if (session == null)
- {
- return null;
- }
- Save(session);
-
- return true;
- }
- catch (Exception ex)
- {
- exception = ex;
- }
- await _logManager.LogAsync(exception);
-
- return false;
- }
-
-
-
-
-
-
-
- public string Provider { get; set; }
-
-
-
-
- public async void Logout()
- {
- Exception exception = null;
- try
- {
- var session = GetSession();
- switch (session.Provider)
- {
- case Constants.FacebookProvider:
- _facebookService.Logout();
- break;
- case Constants.MicrosoftProvider:
- _microsoftService.Logout();
- break;
- case Constants.GoogleProvider:
- _googleService.Logout();
- break;
- }
- CleanSession();
- }
- catch (Exception ex)
- {
- exception = ex;
- }
- if (exception != null)
- {
- await _logManager.LogAsync(exception);
- }
- }
-
- WINDOWS_PHONE_APP
- public async Task<bool> Finalize(WebAuthenticationBrokerContinuationEventArgs args)
- {
- Exception exception = null;
- try
- {
- Session session = null;
- switch (Provider)
- {
- case Constants.FacebookProvider:
- session = await _facebookService.Finalize(args);
- break;
- case Constants.GoogleProvider:
- session = await _googleService.Finalize(args);
- break;
- }
- Save(session);
- return true;
- }
- catch (Exception e)
- {
- exception = e;
- }
- await _logManager.LogAsync(exception);
- return false;
- }
- if
-
-
-
-
-
-
-
-
-
- private string LoadEncryptedSettingValue(string key)
- {
- string value = null;
-
- var protectedBytes = _applicationSettings.LocalSettings[key];
- if (protectedBytes != null)
- {
-
-
-
- value = protectedBytes.ToString();
- }
-
- return value;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- private void SaveEncryptedSettingValue(string key, string value)
- {
- if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value))
- {
-
-
-
-
-
- _applicationSettings.LocalSettings[key] = value;
- }
- }
- }
This class will return null if the session is null, we need to be aware that there isn´t any error and the session is returned or if it is null, the process will be done by the Finalize method.
Here is the class diagram with all classes and interfaces created in this sample:
Now to build the User Interface and because we are using the MVVM pattern, it created a LoginViewModel to do the binding to the LoginView.
The LoginViewModel
-
-
-
- public class LoginViewModel : ViewModelBase
- {
- private readonly ILogManager _logManager;
- private readonly IMessageBoxService _messageBox;
- private readonly INetworkInformationService _networkInformationService;
- private readonly INavigationService _navigationService;
- private readonly ISessionService _sessionService;
- private bool _inProgress;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public LoginViewModel(INavigationService navigationService,
- ISessionService sessionService,
- IMessageBoxService messageBox,
- INetworkInformationService networkInformationService,
- ILogManager logManager)
- {
- _navigationService = navigationService;
- _sessionService = sessionService;
- _messageBox = messageBox;
- _networkInformationService = networkInformationService;
-
- _logManager = logManager;
- LoginCommand = new RelayCommand<string>(LoginAction);
- }
-
-
-
-
-
-
-
- public INavigationService NavigationService
- {
- get { return _navigationService; }
- }
-
-
-
-
-
-
-
- public bool InProgress
- {
- get { return _inProgress; }
- set { Set(() => InProgress, ref _inProgress, value); }
- }
-
-
-
-
-
-
-
- public ICommand LoginCommand { get; private set; }
-
-
-
-
-
-
-
- private async void LoginAction(string provider)
- {
- Exception exception = null;
- bool isToShowMessage = false;
- try
- {
- if (!_networkInformationService.IsNetworkAvailable)
- {
- await _messageBox.ShowAsync("There isn´t network connection.",
- "Authentication Sample",
- new List<string> { "Ok" });
- return;
- }
- if (Constants.GoogleClientId.Contains("<") || Constants.GoogleClientSecret.Contains("<"))
- {
- await _messageBox.ShowAsync("Is missing the google client id and client secret. Search for Constant.cs file.",
- "Authentication Sample",
- new List<string> { "Ok" });
- return;
- }
- if (Constants.FacebookAppId.Contains("<"))
- {
- await _messageBox.ShowAsync("Is missing the facebook client id. Search for Constant.cs file.",
- "Authentication Sample",
- new List<string> { "Ok" });
- return;
- }
- InProgress = true;
- var auth = await _sessionService.LoginAsync(provider);
- if (auth == null)
- {
- return;
- }
- if (!auth.Value)
- {
- await ShowMessage();
- }
- else
- {
- _navigationService.Navigate<MainView>();
- InProgress = false;
- }
-
- InProgress = false;
- }
- catch (Exception ex)
- {
- InProgress = false;
- exception = ex;
- isToShowMessage = true;
- }
- if (isToShowMessage)
- {
- await _messageBox.ShowAsync("Application fails.",
- "Authentication Sample",
- new List<string> { "Ok" });
- }
- if (exception != null)
- {
- await _logManager.LogAsync(exception);
- }
- }
-
- private async Task ShowMessage()
- {
- await _messageBox.ShowAsync("Wasn´t possible to complete the login.",
- "Authentication Sample",
- new List<string>
- {
- "Ok"
- });
- }
-
- #if WINDOWS_PHONE_APP
- public async void Finalize(WebAuthenticationBrokerContinuationEventArgs args)
- {
- var result = await _sessionService.Finalize(args);
- if (!result)
- {
- await ShowMessage();
- }
- else
- {
- _navigationService.Navigate<MainView>();
- InProgress = false;
- }
- }
- #endif
- }
Note: In the LoginAction the parameter provider is the value of the CommandParameter received in LoginCommand, this is set in the login page.
For the final the code, let's see the code for the page.
The LoginPage.xaml
- <Page
- x:Class="AuthenticationSample.UniversalApps.Views.LoginView"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:converters="using:Cimbalino.Toolkit.Converters"
- mc:Ignorable="d">
-
- <Page.DataContext>
- <Binding Mode="OneWay"
- Path="LoginViewModel"
- Source="{StaticResource Locator}" />
- </Page.DataContext>
- <Page.Resources>
- <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
- </Page.Resources>
-
- <Grid x:Name="LayoutRoot" Background="LightGray">
- <Grid.RowDefinitions>
- <RowDefinition Height="{StaticResource HeaderHeigth}" />
- <RowDefinition Height="*" />
- </Grid.RowDefinitions>
-
-
- <StackPanel x:Name="TitlePanel"
- Margin="{StaticResource HeaderMargin}"
- VerticalAlignment="Center" Grid.Row="0">
- <TextBlock FontSize="30"
- Foreground="Black"
- Text="Login"/>
- </StackPanel>
-
-
- <Grid x:Name="ContentPanel"
- VerticalAlignment="Center"
- HorizontalAlignment="Center"
- Grid.Row="1">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto" />
- <RowDefinition Height="80" />
- <RowDefinition Height="80" />
- <RowDefinition Height="80" />
- <RowDefinition Height="80" />
- </Grid.RowDefinitions>
- <TextBlock Grid.Row="0"
- FontSize="20"
- Foreground="Black"
- Text="Use your account"/>
- <Button Grid.Row="1" Width="300"
- Margin="10"
- Command="{Binding LoginCommand}"
- CommandParameter="facebook"
- Background="{StaticResource FacebookBlueBackgroundBrush}" >
- <StackPanel Orientation="Horizontal">
- <TextBlock Text=""
- VerticalAlignment="Center"
- FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"
- HorizontalAlignment="Left"/>
- <TextBlock Margin="10,0,0,0" Text="Facebook"
- HorizontalAlignment="Center"/>
- </StackPanel>
- </Button>
- <Button Grid.Row="3"
- Margin="10" Width="300"
- Command="{Binding LoginCommand}"
- CommandParameter="microsoft"
- Background="{StaticResource MicrosoftBlueBackgroundBrush}" >
- <StackPanel Orientation="Horizontal">
- <TextBlock Text=""
- VerticalAlignment="Center"
- FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"
- HorizontalAlignment="Left"/>
- <TextBlock Margin="10,0,0,0" Text="Microsoft"
- HorizontalAlignment="Center"/>
- </StackPanel>
- </Button>
- <Button Grid.Row="2" Width="300"
- Margin="10"
- Command="{Binding LoginCommand}"
- CommandParameter="google"
- Background="{StaticResource GoogleRedBackgroundBrush}" >
- <StackPanel Orientation="Horizontal">
- <TextBlock Text=""
- VerticalAlignment="Center"
- FontFamily="/Fonts/fontawesome-webfont.ttf#FontAwesome"
- HorizontalAlignment="Left"/>
- <TextBlock Margin="10,0,0,0" Text="Google"
- HorizontalAlignment="Center"/>
- </StackPanel>
- </Button>
- </Grid>
- <Grid Visibility="{Binding InProgress, Converter={StaticResource BooleanToVisibilityConverter}}"
- Grid.Row="0"
- Grid.RowSpan="2">
- <Rectangle
- Fill="Black"
- Opacity="0.75" />
- <TextBlock
- HorizontalAlignment="Center"
- VerticalAlignment="Center"
- Text="Auth..." />
- <ProgressBar IsIndeterminate="True" IsEnabled="True" Margin="0,60,0,0"/>
- </Grid>
-
- </Grid>
- </Page>
For use in the same page in Windows 8.1 Store apps and Windows Phone 8.1 Runtime was used some style.
The LoginView.xaml.cs
-
-
-
- #if WINDOWS_PHONE_APP
- public sealed partial class LoginView : Page, IWebAuthenticationContinuable
- #else
- public sealed partial class LoginView : Page
- #endif
- {
-
-
-
- public LoginView()
- {
- InitializeComponent();
- }
-
-
-
-
-
-
-
- protected override void OnNavigatedTo(NavigationEventArgs e)
- {
- var viewModel = (LoginViewModel)DataContext;
- viewModel.NavigationService.RemoveBackEntry();
- base.OnNavigatedTo(e);
- }
-
- #if WINDOWS_PHONE_APP
-
-
-
-
-
- public void ContinueWebAuthentication(Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs args)
- {
- var viewModel = (LoginViewModel)DataContext;
- viewModel.Finalize(args);
- }
- #endif
- }
ConclusionIn conclusion, the flow is completely different for each platform and it has some impact in the development and in sharing the code. Maybe, for this reason there isn't any package for Windows Phone 8.1 that uses authentication.
Source CodeThe source code can be found
here.Source Code Files
- IFacebookService interface for FacebookService
- IGoogleService interface for GoogleService
- ILogManager interface for LogManager
- IMicrosoftService interface for MicrosoftService
- ISessionProvider interface for all providers interface (common methods)
- ISessionService for SessionService
- Session class for save the information about the session (provider, token, expired date)
- FacebookService class that has all logic about the login / logout using Facebook provider
- GoogleService class that has all logic about the login / logout using Google provider
- MicrosoftService class that has all logic about the login / logout using Microsoft provider
- SessionService class for manage login/logout (it will use all services provides described before)
- LoginViewModel class for binding to LoginView.xaml
- LoginView class that represent the page for login
- MainViewModel class for binding to MainView.xaml
- MainView class that appear after the login is ok
- ViewModelLocator class contains static references to all the view model in application and provides an entry point for the bindings.
The solution
Build the Sample
- Start Visual Studio Express 2012 for Windows 8 and select File > Open > Project/Solution.
- Go to the directory in which you unzipped the sample. Go to the directory named for the sample and double-click the Visual Studio Express 2012 for Windows 8 Solution (.sln) file.
- Press F7 or use Build > Build Solution to build the sample.
Note: you can use Visual Studio 2013 in Windows 8.1.
Run the sample
To debug the app and then run it, press F5 or use Debug > Start Debugging. To run the app without debugging, press Ctrl+F5 or use Debug > Start Without Debugging
Output
The output for each target is:
Login Page
Microsoft Authentication
Facebook Authentication
Google Authentication
Login View
Facebook Authentication
Google Authentication