Mobile Zone is brought to you in partnership with:

Quality Assurance Analyst, Windows Phone developer and father. Contributing author for Windows Phone 8 in Action from Manning Publications. Adam is a DZone MVB and is not an employee of DZone and has posted 15 posts at DZone. You can read more from them at their website. View Full User Profile

Windows Phone: Enabling Authentication With Social Media Sites

07.16.2012
| 2610 views |
  • submit to reddit

Enabling authentication using social media sites (Facebook, Twitter, Live and Google) in a Windows Phone app can be a bit of a pain. Implementing one is fairly straight forward once you understand the workflow back and forth.  Once you add multiple authentication sources can lead to a lot of troubleshooting and repeated code. Infragistics has come up with nice solution to this. In their NetAdvantage for Windows Phone product is their ACS Control.

Because all 4 sites use oAuth for authentication (oAuth 1.0a for Twitter and oAuth 2.0 for Live ID, Gmail and Facebook) a combined control makes alot of sense. Once the user is logged in, user data is available to create a user profile or any other you need it for. The returned Authentication Token is also available to make subsequent calls back to the authenticating sites. Since this control only covers authentication, you will need to implement your own solution if you need to interact with the webservice.

First thing you will need to do is head over to the Infragistics NetAdvantage for Windows Phone page and download the trial. While that is installing, you should then head over to Facebook and create an app if you havn't already.

Facebook        https://developers.facebook.com/apps

Other supported providers are:

Twitter            https://dev.twitter.com/apps/new
Live ID            https://manage.dev.live.com/AddApplication.aspx
Google            https://code.google.com/apis/console/?pli=1


Once you have the App ID/API Key and App Secret values, you can open Visual Studio 2010 and either start a new solution and follow along or download the completed solution and open it. I suggest you download the completed solution as it contains parts I did not cover that are important to make it function.

Once created, Add a reference to InfragisticsWP7.Controls.Authentication.v12.1.dll. On my 64-bit machine, it installed in "C:\Program Files (x86)\Infragistics\NetAdvantage 2012.1\Windows Phone\Bin\".

Now open the MainPage.xaml file and add the following to the "phone:PhoneApplicationPage" tag at the top of the file: 

xmlns:ig="clr-namespace:Infragistics.Controls.Authentication;assembly=InfragisticsWP7.Controls.Authentication.v12.1"

Now we add the following to the "ContentPanel" Grid:

<ig:XamAccessControlCatalog x:Name="FacebookLogin" >
	<ig:XamAccessControlCatalog.AuthenticationServices>
		<ig:AuthenticationService 
			Provider="Facebook"
			ClientId="xxxxxxxxxxxxxxxx"
			RedirectUri="http://www.facebook.com/connect/login_success.html">
			<ig:AuthenticationService.Scopes>
				<System:String>email</System:String>
			</ig:AuthenticationService.Scopes>
		</ig:AuthenticationService>
	</ig:XamAccessControlCatalog.AuthenticationServices>
</ig:XamAccessControlCatalog>

 This xaml enables a list of all the authentication providers you have configured. This example only uses Facebook but you can add others.

I am skipping a few of the finer details of configuring the ACS Control. You can find documentation and some more example code here:

< http://help.infragistics.com/Help/NetAdvantage/WindowsPhone/2012.1/CLR4.0/html/xamAccessControl_Using_xamAccessControl.html >

Now in the MainPage.xaml.cs file we will modify the constructor by adding 3 event handlers and their code:

public MainPage()
{
	InitializeComponent();

	FacebookLogin.AuthenticationCompleted += LoginAuthenticationCompleted;
	FacebookLogin.AuthenticationDenied += LoginAuthenticationDenied;
	FacebookLogin.AuthenticationException += LoginAuthenticationException;

}
void LoginAuthenticationException(object sender, Infragistics.Controls.Authentication.AuthenticationExceptionEventArgs e)
{
	MessageBox.Show("Authentication Exception:" + e.Exception.Message);
}

void LoginAuthenticationDenied(object sender, Infragistics.Controls.Authentication.AuthenticationDeniedEventArgs e)
{
	MessageBox.Show("Authentication Denied:" + e.AuthenticationDeniedResult.Error);
}

void LoginAuthenticationCompleted(object sender, Infragistics.Controls.Authentication.AuthenticationCompletedEventArgs e)
{
	MessageBox.Show("Authentication Completed:" + e.AuthenticationResult.AuthenticationToken);
	FillViewModel(e.AuthenticationResult.UserData);
	NavigationService.Navigate(new Uri("/Views/UserDetails.xaml", UriKind.Relative));
}

 In order to display the returned userdata, I used something that is not quite MVVM because there is no model but for simplicity sake the model was left it out.

To make theViewModel available to all pages, we define the ViewModel property in App.xaml.cs:

private static PersonViewModel viewModel = null;
public static PersonViewModel ViewModel
{
	get
	{
		// Delay creation of the view model until necessary
		if (viewModel == null)  
			viewModel = new PersonViewModel();

		return viewModel;
	}
}

The above code checks the viewModel property to see if it has been set, if not it creates a instance if the ViewModel type. Now when we successfully authenticate and the AuthenticationCompleted event fires, we call the FillViewModel function that populates the ViewModel"

private void FillViewModel(Dictionary<string, string> userData)
{
	App.ViewModel.Username = userData.Single(r => r.Key == "username").Value;
	App.ViewModel.Name = userData.Single(r => r.Key == "name").Value;
	App.ViewModel.Gender = userData.Single(r => r.Key == "gender").Value;
	App.ViewModel.Email = userData.Single(r => r.Key == "email").Value;
	App.ViewModel.Link = userData.Single(r => r.Key == "link").Value;
	App.ViewModel.Bio = userData.Single(r => r.Key == "bio").Value;
	App.ViewModel.Quotes = userData.Single(r => r.Key == "quotes").Value;
}

 Now that the App.ViewModel property has been populated with the userdata, we can now  add a View called UserData.xaml and open the UserDetails.xaml.cs file. In the constructor, set the DataContext to the App.ViewModel property.

public UserDetails()
{
	InitializeComponent();
	DataContext = App.ViewModel;
}

Now we add our TextBlocks in a ScrollViewer and StackPanel. This allws the data the be stacked nicely and still scroll if you have more than one screens worth of data:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
	<ScrollViewer>
		<StackPanel>
			<TextBlock Name="UserNameLabel" Text="Username" Foreground="{StaticResource PhoneAccentBrush}" FontSize="{StaticResource PhoneFontSizeMedium}"/>
			<TextBlock Name="UserNameTextBlock" Text="{Binding Username}" Margin="10" FontSize="{StaticResource PhoneFontSizeNormal}" />
			
			<TextBlock Name="GenderLabel" Text="Gender" Foreground="{StaticResource PhoneAccentBrush}" FontSize="{StaticResource PhoneFontSizeMedium}"/>
			<TextBlock Name="GenderTextBlock" Text="{Binding Gender}" Margin="10" FontSize="{StaticResource PhoneFontSizeNormal}" />

			<TextBlock Name="EmailLabel" Text="Email" Foreground="{StaticResource PhoneAccentBrush}" FontSize="{StaticResource PhoneFontSizeMedium}"/>
			<StackPanel Orientation="Horizontal">
				<TextBlock Name="EmailTextBlock" Text="{Binding Email}" Margin="10" FontSize="{StaticResource PhoneFontSizeNormal}" Tap="EmailImage_Tap"  />
					<Image Height="48" Name="EmailImage" Stretch="Fill" Width="48" Source="/Images/Mail.png" Tap="EmailImage_Tap" />
			</StackPanel>
			
			<TextBlock Name="LinkLabel" Text="Link" Foreground="{StaticResource PhoneAccentBrush}" FontSize="{StaticResource PhoneFontSizeMedium}"/>
			<StackPanel Orientation="Horizontal">
				<TextBlock Name="LinkTextBlock" Margin="10" Text="{Binding Link}" FontSize="{StaticResource PhoneFontSizeNormal}" Tap="LinkImage_Tap"/>
				<Image Height="48" Name="LinkImage" Stretch="Fill" Width="48" Source="/Images/Internet Explorer.png" Tap="LinkImage_Tap" />
			</StackPanel>

			<TextBlock Name="BioLabel" Text="Bio" Foreground="{StaticResource PhoneAccentBrush}" FontSize="{StaticResource PhoneFontSizeMedium}"/>
			<TextBlock Name="BioTextBlock" Text="{Binding Bio}" TextWrapping="Wrap" Margin="10" FontSize="{StaticResource PhoneFontSizeNormal}" />

			<TextBlock Name="QuotesLabel" Text="Quotes" Foreground="{StaticResource PhoneAccentBrush}" FontSize="{StaticResource PhoneFontSizeMedium}"/>
			<TextBlock Name="QuotesTextBlock" Text="{Binding Quotes}" TextWrapping="Wrap" Margin="10" FontSize="{StaticResource PhoneFontSizeNormal}" />
		</StackPanel>
	</ScrollViewer>
</Grid>

 In this example, I made the email and Facebook page addresses tap-able. Tapping the email address opens a ComposeEmailTask while the Webpage address opens a WebBrowserTask. I have checked to make sure that the url or email address are actually filled. I also wrap each one in a try-catch structure and catch a specific event. If you quickly tap a button or like that triggers and Tasks, an InvalidOperationException is thrown. I specifically catch this exception and ignore it. I then allow any other exceptions to be thrown. I honestly don't know if this is a good practice but I use it in all of my apps to prevent this specific error.

private void LinkImage_Tap(object sender, GestureEventArgs e)
{
	try
	{
		if (!String.IsNullOrEmpty(LinkTextBlock.Text))
		{
			var browserTask = new WebBrowserTask();
			browserTask.Uri = new Uri(LinkTextBlock.Text, UriKind.Absolute);
			browserTask.Show();
		}
	}
	catch(InvalidOperationException ex)
	{
		if (ex.Message != "Navigation is not allowed when the task is not in the foreground. Error: -2147220990")
			throw ex;
	}
}

private void EmailImage_Tap(object sender, GestureEventArgs e)
{
	try
	{
		if (!String.IsNullOrEmpty(EmailTextBlock.Text))
		{
			var mailTask = new EmailComposeTask();
			mailTask.To = EmailTextBlock.Text;
			mailTask.Show();
		}
	}
	catch (InvalidOperationException ex)
	{
		if (ex.Message != "Navigation is not allowed when the task is not in the foreground. Error: -2147220990")
			throw ex;
	}
}

Now that we have walked through the way this example you can see that it takes significantly more work to display the info about the user than to actually get it. All it takes is to add the control to your xaml file and listen for the 3 events. Adding the ability to collect info, populate signup forms and profile pages is extremely simple using the Infragistics control.

While this is a paid control, the time it saves in getting users logged in and getting the ever so valuable user data from them more than offsets the $99 price tag. I am currently using the trial version and will be using it in future apps I write. Using the ShareStatusTask and ShareLinkTasks work for some cases and I have used them but if you want your apps name attributed as helping you post, you must authenticate and get the users permission. In my mind, if I can save a couple of hours implementing authentication in a single app it will pay for itself.

AttachmentSize
AuthExample.zip34.63 KB
Published at DZone with permission of Adam Benoit, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)