Mobile Zone is brought to you in partnership with:

Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

Flickr API for Windows Phone 7 – Part 16 – UI – User Authentication

10.24.2010
| 6668 views |
  • submit to reddit

So it’s about time I start working on the UI for the Flickr for Windows Phone 7 application. Now that you have the core engine ready, it’s time to put it to life and show what it’s capable of. In this article I am going to show how I set the main layout and how I go through the process of user authentication.

The main window is based on a Panorama control that will contain sections for photos, favorites, groups and what not. That way it is easier for the user to navigate through existing content without having to load new pages for each new selection.

For now, the XAML markup for the main page is quite simple:

<phone:PhoneApplicationPage
x:Class="FlickrWP7.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
shell:SystemTray.IsVisible="True">

<Grid x:Name="LayoutRoot" Background="Transparent">
<controls:Panorama Width="Auto" Height="Auto" Title="Flickr">
<controls:PanoramaItem Header="profile"></controls:PanoramaItem>
</controls:Panorama>
</Grid>
</phone:PhoneApplicationPage>

Rendered, it looks like this:

But besides the actual main page, I’ve added some additional pages. These pages are:

•    Check.xaml
•    AuthLocation.xaml
•    AuthBrowser.xaml

Now here is why I needed these pages. Before the application starts, I need to check whether the user is authenticated and is registered with this application. That’s why there is the Check.xaml page. It is a page that has no other purpose but be an intermediary link – with its help I can check whether the user is registered with the current application instance:

using System;
using Microsoft.Phone.Controls;
using System.Collections.Generic;
using System.Windows;

namespace FlickrWP7
{
public partial class Check : PhoneApplicationPage
{
public Check()
{
InitializeComponent();
}

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
try
{
if (System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["token"].ToString() != “0”)
{
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
else
{
NavigationService.Navigate(new Uri("/AuthLocation.xaml", UriKind.Relative));
}
}
catch (KeyNotFoundException)
{
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Add("token", “0”);
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Save();
NavigationService.Navigate(new Uri("/AuthLocation.xaml", UriKind.Relative));
}
}
}
}

Before I am going to break it down, take a look at this graphic that shows how Flickr performs user authentication in a third-party application:

First of all, the user has to authorize the application - he/she should agree that the application will be able to access his/her private content (e.g. photos and profile information). When the user gives that permission, there is a mini-token generated that later on should be entered manually by the user in the application. Based on the mini-token, there will be a full token generated that will be used across other methods for the registered user.

With the code I’ve shown above, I am checking whether the current application has a user already registered – the full token presence indicates that. If a KeyNotFoundException is encountered - this is the first time the application is launched, therefore a setting named token should be created. The default value for this setting is set to zero since it is the first time the setting is called, therefore there is no actual token registered. A question that might be asked here is why I set the value to zero instead of null. The answer is simple - I cannot use if statements to check against null values, since a NullReferenceException will be raised every time a null object is called. To avoid handling another exception type, I am simply using a shorter value that I can check against.

If there is a token setting, I am checking whether it is zero or not. If it is – this means that there is still no authenticated user registered with the application and I should redirect the user to the authentication page. This can happen if the setting is created but the user never entered a correct mini-token. If the token is not zero, then there is already a user that authorized the app and I can take the user directly to the main page.

The XAML layout for Check.xaml is as simple as it can get, because the UI doesn’t play a significant role here:

<phone:PhoneApplicationPage
x:Class="FlickrWP7.Check"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True"
Loaded="PhoneApplicationPage_Loaded">

<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock x:Name="PageTitle" Text="verifying..." Margin="84,331,47,329" Style="{StaticResource PhoneTextTitle1Style}" />
</Grid>
</Grid>
</phone:PhoneApplicationPage>

Now there is AuthLocation.xaml – that’s the place where new users end up. That’s the page where the user has to enter the mini-token. The XAML for it is this:

<phone:PhoneApplicationPage
x:Class="FlickrWP7.AuthLocation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True"
Loaded="PhoneApplicationPage_Loaded">

<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="FLICKR FOR WINDOWS PHONE 7" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="authenticate" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Text="Navigate to the URL below on a desktop PC to get the mini-token:" Width="430" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,38,12,511" />
<TextBlock Height="30" HorizontalAlignment="Left" Margin="13,102,0,0" FontWeight="Bold" VerticalAlignment="Top" Text="www.flickr.com/auth-72157625015033978" />
<TextBlock Margin="12,152,14,371" Style="{StaticResource PhoneTextNormalStyle}" Text="Or click on the button below to navigate on the phone. You will need to provide your Flickr user credentials." TextWrapping="Wrap" Width="430" />
<Button Content="Navigate to URL" Height="72" HorizontalAlignment="Left" Margin="0,242,0,0" Name="browserNavigate" VerticalAlignment="Top" Width="450" Click="browserNavigate_Click" />
<TextBlock Margin="12,330,14,193" Style="{StaticResource PhoneTextNormalStyle}" Text="Once you authorize the application, enter the mini-token in the boxes below:" TextWrapping="Wrap" Width="430" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="43,399,0,0" Name="txtTokenA" MaxLength="3" VerticalAlignment="Top" Width="114" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="163,399,0,0" MaxLength="3" Name="txtTokenB" VerticalAlignment="Top" Width="114" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="283,399,0,0" MaxLength="3" Name="txtTokenC" VerticalAlignment="Top" Width="114" />
<TextBlock Margin="155,420,282,154" Style="{StaticResource PhoneTextNormalStyle}" Text="-" TextWrapping="Wrap" />
<TextBlock Margin="277,420,160,154" Style="{StaticResource PhoneTextNormalStyle}" Text="-" TextWrapping="Wrap" />
<Button Content="Use Token" Height="72" HorizontalAlignment="Left" Margin="0,477,0,0" Name="button1" VerticalAlignment="Top" Width="450" Click="button1_Click" />
</Grid>
</Grid>

</phone:PhoneApplicationPage>

Although it seems like the layout is quite complicated, when it is rendered you can see that it really is a set of TextBlocks and TextBoxes (plus two Buttons):

There is a URL where the user has to navigate manually to get the mini-token – it is given to you as the application developer when you register your application and get an API key. You cannot automate this process, since the user has to manually click on the authorization button.

And here you have two options – either let the user get the mini-token on a PC or directly in the app. As you can see, I mention in the current page that there is the PC option, but by clicking on Navigate to URL, the user is able to perform the same task on the device. And that is a reasonable option - when working on a mobile application you should always consider one thing - there is NO access to a PC - the user can be in a remote location or simply doesn't have a full-sized computer at hand.

What it does is open a page with a WebBrowser control that automatically navigates to the URL specified above. That is AuthBrowser.xaml:

<phone:PhoneApplicationPage
x:Class="FlickrWP7.AuthBrowser"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True"
Loaded="PhoneApplicationPage_Loaded">

<Grid x:Name="LayoutRoot" Background="Transparent">
<phone:WebBrowser Name="authBrowser" Margin="0,0,0,97"></phone:WebBrowser>
<Button Content="Back to Auth" Height="72" HorizontalAlignment="Left" Margin="12,684,0,0" Name="btnBack" VerticalAlignment="Top" Width="456" Click="btnBack_Click" />
</Grid>

</phone:PhoneApplicationPage>

It requires the user to enter the Flickr ID and password and then authorize the app:

The code for the actual page (written in C#, obviously), is this:

using System;
using Microsoft.Phone.Controls;
using System.Windows;

namespace FlickrWP7
{
public partial class AuthBrowser : PhoneApplicationPage
{
public AuthBrowser()
{
InitializeComponent();
}

private void btnBack_Click(object sender, RoutedEventArgs e)
{
NavigationService.GoBack();
}

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
authBrowser.Navigate(new Uri("http://www.flickr.com/auth-72157625015033978"));
}
}
}

The browser navigates to the page required by the application. It is safe to use a hard-coded URL for now because it is not tied to a specific user but rather is given on a “one URL per application” basis.

The button navigates back to the Authentication page – not in the browser, but in the application, so that the user can enter the obtained mini-token.

Now let’s look at the code-behind part for AuthLocation.xaml:

using System;
using System.Windows;
using System.Json;
using Microsoft.Phone.Controls;
using System.Collections.Generic;

namespace FlickrWP7
{
public partial class AuthLocation : PhoneApplicationPage
{
public AuthLocation()
{
InitializeComponent();
}

private void browserNavigate_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/AuthBrowser.xaml",UriKind.Relative));
}

private void button1_Click(object sender, RoutedEventArgs e)
{
string miniToken = string.Format("{0}-{1}-{2}", txtTokenA.Text, txtTokenB.Text, txtTokenC.Text);

Dictionary<string,string> param = new Dictionary<string,string>();
param.Add("mini_token",miniToken);
param.Add("format", "json");
param.Add("api_key", App.API_KEY);
param.Add("method", "flickr.auth.getFullToken");


Core.Authentication.GetSignature(param, App.SECRET, () =>
{
Core.Authentication.GetFullToken(miniToken, Core.Authentication.Signature, App.API_KEY, () =>
{
string temp = string.Empty;

try
{
JsonObject json = JsonObject.Parse(Core.Json.MakeValidJson(Core.Authentication.FullToken)) as JsonObject;
JsonValue value = null;
json.TryGetValue("auth", out value);
json = JsonObject.Parse(value.ToString()) as JsonObject;
json.TryGetValue("token", out value);
json = JsonObject.Parse(value.ToString()) as JsonObject;
json.TryGetValue("_content", out value);
temp = value.ToString().Remove(0, 1);
temp = temp.Remove(temp.Length - 1, 1);
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["token"] = temp;
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Save();

json = JsonObject.Parse(Core.Json.MakeValidJson(Core.Authentication.FullToken)) as JsonObject;
json.TryGetValue("auth", out value);
json = JsonObject.Parse(value.ToString()) as JsonObject;
json.TryGetValue("user", out value);
json = JsonObject.Parse(value.ToString()) as JsonObject;
json.TryGetValue("username", out value);
temp = value.ToString().Remove(0, 1);
temp = temp.Remove(temp.Length - 1, 1);


try
{
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["user"] = temp;
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Save();
}
catch
{
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Add("user", "0");
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["user"] = temp;
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Save();
}

NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
catch
{
MessageBox.Show("Cannot authorize the user with the specified token. Please try again later or use a different token.");
}
});
});

}
}
}

When the user enters the mini-token in the fields provided on the page, I combine them in a single string and try to obtain a signature and then a full token for the specified data. If the request fails for some reason (the most frequent reason is going to be an invalid mini-token), I am displaying a message notifying the user about the exception. The exception is mainly determined by the JSON data that is obtained on getting the full token. If the needed fields are not found, the call automatically fails.

NOTE: API_KEY and SECRET are string contstants defined in the App.xaml.cs - available globally through the application.

When JSON data is parsed, I am extracting the username and the full token. These are the two elements required to get some more user information and execute other Flickr API calls. Notice that the same as for the token setting, there is a setting for the username, called user.

For today, I am going to stop here – now there is a basic authentication scheme present in the application and now it is possible to actually work on the data linked to the account.

You can download the new pages I've created below: