Introduction
The Windows Phone 8.1 platform provides programmatic access to support Hotspot authentication via APIs in the Windows.Networking.NetworkOperators namespace. In this article, we will learn how to use these APIs to perform Hotspot authentication.
Wireless Internet Service Provider roaming, called WISPr, is a protocol that allows users to roam among wireless Internet service providers. Mobile operators can implement WISPr protocol to allow devices to seamlessly log in to hotspots without the need for the user to interact with a captive portal.
Let us take a quick look at the classes/APIs of interest to build Hotspot authentication in our application.
- HotspotAuthenticationContext class: This class provides the authentication context that contains details of the current authentication attempt and provides methods to perform the authentication.
- HotspotAuthenticationContext.IssueCredentialsAsync method: This method asynchronously provides credentials for Hotspot authentication. This method is only available to mobile operator apps and Windows Store given privileged access by mobile network operators.
- HotspotAuthenticationContext.WirelessNetworkId property: This property gets the SSID of the WLAN access point. This property is only available to mobile operator apps and Windows Store given privileged access by mobile network operators.
- HotspotAuthenticationContext.NetworkAdapter property: This property gets the network interface that is connected to the WLAN access point of the Hotspot. This property is only available to mobile operator apps and Windows Store-given privileged access by mobile network operators.
WISPr imposes no requirements for “Windows Phone” access points. Windows Phone does not provide native WISPr authentication support, unlike Windows, which supports authentication via the HotSpotAuthenticationContext.IssueCredentialsAsync API. Hence, Windows Phone apps will need to implement the HotSpotAuthenticationContext.IssueCredentialsAsync API in the app and call the HotSpotAuthenticationContext.SkipAuthentication method. Unlike Windows Store applications, Windows Phone applications have only WirelessNetworkId and NetworkAdapter as valid properties of a HotspotAuthenticationContext object.
Building an application that does Hotspot authentication requires the application to be blessed by the mobile carrier because it needs provisioning information that only the mobile carrier can provide.
Hands On
Let us create a simple application to use To use the Hotspot Authentication APIs.
Create a new Visual Studio 2013 project by using the Windows Phone Blank App template. In the default page created, add a few controls, as follows:
Figure 1: Adding controls to the default page
The XAML code behind is presented here:
<Page x_Class="App10.MainPage" xmlns_x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns_local="using:App10" xmlns_d="http://schemas.microsoft.com/expression/ blend/2008" xmlns_mc="http://schemas.openxmlformats.org/ markup-compatibility/2006" mc_Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid> <Button x_Name="buttonProvision" Content="Provision" HorizontalAlignment="Left" Margin="130,69,0,0" VerticalAlignment="Top" /> <TextBlock HorizontalAlignment="Left" Margin="130,600,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Height="30" Width="238"/> <TextBlock x_Name="textBlockStatus" HorizontalAlignment="Left" Margin="130,600,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="30" Width="238" ScrollViewer.HorizontalScrollBarVisibility="Auto"/> <TextBlock HorizontalAlignment="Left" Margin="42,600,0,0" TextWrapping="Wrap" Text="Status" VerticalAlignment="Top"/> <Button x_Name="buttonAuthenticateThroughBackground" Content="Authenticate through background task" HorizontalAlignment="Left" Margin="28,186,0,0" VerticalAlignment="Top" Width="340" /> <Rectangle Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="222" Margin="28,326,0,0" Stroke="Black" VerticalAlignment="Top" Width="340" Opacity="0.25"/> <Button x_Name="buttonAuthenticate" Content="Authenticate" HorizontalAlignment="Left" Margin="130,414,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.695,0.172" Width="131" /> <Button x_Name="buttonSkip" Content="Skip" HorizontalAlignment="Left" Margin="130,457,0,0" VerticalAlignment="Top" Width="131" /> <Button x_Name="buttonAbort" Content="Abort" HorizontalAlignment="Left" Margin="130,500,0,0" VerticalAlignment="Top" Width="131" /> <CheckBox x_Name="checkBoxForegroundApp" Content="ForegroundApp" HorizontalAlignment="Left" Margin="84,362,0,0" VerticalAlignment="Top" /> </Grid> </Page>
Next, we update the application manifest file to declare the background task that the application will execute.
Right-click the Project name in Solution Explorer and click Properties.
Figure 2: Selecting Properties
Click the “Package Manifest…” button and navigate to the Declarations tab.
Figure 3: The Declarations tab
Add a BackgroundTask from the Available Declarations drop-down and click Add. Select “System Event” under the properties section and specify an entry point as a class (which we will later define) in the Entry point field.
Next, we will create a file that contains the Provisioning Data for a Hotspot. Note that the structure of this file is well defined and has to be copiedas-is.
Add a new XML file, called ProvisioningData.xml, to the solution with the following contents.
<?xml version="1.0" encoding="utf-8"?> <CarrierProvisioning > <Global> <!-- Adjust the Carrier ID to fit your own ID. Refer to the MSDN documentation about Carrier ID's. --> <CarrierId>{11111111-1111-1111-1111-111111111111}</CarrierId> <!-- Adjust the Susbscriber ID. Refer to the MSDN documentation about Subscriber ID's. --> <SubscriberId>1234567890</SubscriberId> </Global> <WLANProfiles> <WLANProfile > <!-- Adjust the profile name to have a human readable network name. By default this equals the SSID. --> <name>Contoso Wi-Fi</name> <SSIDConfig> <SSID> <!-- Adjust the SSID name to fit the SSID of the hotspot. --> <name>contosowifi</name> </SSID> </SSIDConfig> <MSM> <security> <authEncryption> <authentication>open</authentication> <encryption>none</encryption> <useOneX>false</useOneX> </authEncryption> <HotspotProfile > <ExtAuth> <!-- Adjust the extension ID to match the package family name of the application running the background task handler. --> <ExtensionId>7a043ce1-b464-41d5-9805- 76694351be64_h2fgybhm3had8</ExtensionId> </ExtAuth> </HotspotProfile> </security> </MSM> </WLANProfile> </WLANProfiles> </CarrierProvisioning>
The Extension ID is the same ID we will notice in the Application Manifest as “Package Family Name” under the Packaging tab.
Figure 4: The Application Manifest is “Package Family Name” under the Packaging tab
Now, we will implement a helper class for storing and retrieving the configuration.
Add a new class, called “Config”, to the project. For simplicity, we will hard code a few properties and provide properties for capturing the name of the background helper tasks and authentication token.
//Config.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Windows.Storage; namespace App10 { public class Config { public static string AuthenticationHost { get { return "signin.contoso.com"; } } public static string UserName { get { return "username"; } } public static string Password { get { return "PassW@rd"; } } public static string ExtraParameters { get { return ""; } } // This flag is set by the foreground app to toggle // authentication to be done by the background task handler. public static bool AuthenticateThroughBackgroundTask { get { object value; if (ApplicationData.Current.LocalSettings.Values. TryGetValue("background", out value) && value is bool) return (bool)value; return true; // default value } set { ApplicationData.Current.LocalSettings.Values ["background"] = value; } } // This item is set by the background task handler to pass // an authentication event token to the foreground app. public static string AuthenticationToken { get { object value; if (ApplicationData.Current.LocalSettings.Values. TryGetValue("token", out value) && value is string) return value as string; return ""; } set { ApplicationData.Current.LocalSettings.Values ["token"] = value; } } } }
Next, we implement the Background authentication task. Add another class, called BackgroundAuthenticationTask.cs, to the project:
//BackgroundAuthenticationTask.cs using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using Windows.ApplicationModel.Background; using Windows.Data.Xml.Dom; using Windows.Networking.NetworkOperators; using Windows.UI.Notifications; namespace App10 { class BackgroundAuthenticationTask : IBackgroundTask { private const string _foregroundAppId = "HotspotAuthenticationApp.App"; private BackgroundTaskDeferral _deferral; public void Run(IBackgroundTaskInstance taskInstance) { // Do the background task activity. // First, get the authentication context. var details = taskInstance.TriggerDetails as HotspotAuthenticationEventDetails; HotspotAuthenticationContext context; if (!HotspotAuthenticationContext. TryGetAuthenticationContext (details.EventToken, out context)) { // Event is not of interest. Abort. return; } byte[] ssid = context.WirelessNetworkId; // Get configuration from application storage. // Check if authentication is handled by foreground app. if (!Config.AuthenticateThroughBackgroundTask) { // Pass event token to application Config.AuthenticationToken = details.EventToken; // TriggerAttentionRequired function throws // NotImplementedException on phone, we use // regular Toast Notification to notify user about // the authentication, Tapping on the notification // will launch the application where user can // complete the authentication. var toastXml = ToastNotificationManager.GetTemplateContent (ToastTemplateType.ToastText01); toastXml.GetElementsByTagName("text")[0]. AppendChild(toastXml.CreateTextNode("Auth by foreground")); IXmlNode toastNode = toastXml.SelectSingleNode("/toast"); ((XmlElement)toastNode).SetAttribute("launch", "AuthByForeground"); dynamic toast = new ToastNotification(toastXml); Type typeofToastNotification = toast.GetType(); PropertyInfo tagProperty = typeofToastNotification.GetRuntimeProperty("Tag"); PropertyInfo groupProperty = typeofToastNotification.GetRuntimeProperty("Group"); if (tagProperty != null) toast.Tag = "AuthByForeground"; if (groupProperty != null) toast.Group = "HotspotAuthAPI"; var notification = ToastNotificationManager.CreateToastNotifier(); notification.Show(toast); return; } // Handle authentication in background task. // Before calling an asynchronous API from the background task, // get the deferral object from the task instance. _deferral = taskInstance.GetDeferral(); // Finally, call SkipAuthentication to indicate that we // are not doing native WISPr authentication. // This call also serves the purpose of indicating a // successful authentication. context.SkipAuthentication(); _deferral.Complete(); } } }
In this class, we have code that is invoked when the background task is called. We also have created logic to display a Toast Notification.
Now, we are ready to wire up the UI to user actions.
In the code behind for MainPage (MainPage.xaml.cs), we will add the following variables to the class.
public sealed partial class MainPage : Page { string taskName = "AuthenticationBackgroundTask"; string backgroundEntryTaskPoint = "HotspotAuthenticationTask.AuthenticationTask"; HotspotAuthenticationContext authenticationContext; public const string BackgroundTaskEntryPoint = "HotspotAuthenticationTask.AuthenticationTask"; public const string BackgroundTaskName = "AuthenticationBackgroundTask"; private bool HasRegisteredBackgroundTaskHandler = false; public ForegroundAuthenticationDelegate ForegroundAuthenticationCallback; CoreDispatcher coreDispatcher = Windows.UI.Xaml.Window.Current.CoreWindow.Dispatcher; // A delegate type for hooking up foreground // authentication notifications. public delegate void ForegroundAuthenticationDelegate (object sender, EventArgs e); public MainPage()
Next, we implement the click event handler for the Provision button.
private void buttonProvision_Click(object sender, RoutedEventArgs e) { BackgroundAccessStatus status = BackgroundExecutionManager.RequestAccessAsync().AsTask(). GetAwaiter().GetResult(); if (status == BackgroundAccessStatus.Denied) { textBlockStatus.Text = "Access denied while trying Async Access"; return; } var taskBuilder = new BackgroundTaskBuilder(); // Create a new NetworkOperatorHotspotAuthentication trigger. var trigger = new NetworkOperatorHotspotAuthenticationTrigger(); // Associate the NetworkOperatorHotspotAuthentication trigger // with the background task builder. taskBuilder.SetTrigger(trigger); // Specify the background task to run when the trigger fires. taskBuilder.TaskEntryPoint = this.backgroundEntryTaskPoint; // Name the background task. taskBuilder.Name = this.taskName; // Register the background task. var task = taskBuilder.Register(); buttonProvision.IsEnabled = false; }
Next, we add an event handler for Background task completion.
public async void OnBackgroundTaskCompleted(IBackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs e) { // Update the UI with progress reported by the background task. await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() => { if ((sender != null) && (e != null)) { // Update the UI with the completion status of the // background task. // The Run method of the background task sets this status. if (sender.Name == BackgroundTaskName) { textBlockStatus.Text = "Background task completed"; // Signal callback for foreground authentication if (!Config.AuthenticateThroughBackgroundTask && ForegroundAuthenticationCallback != null) { ForegroundAuthenticationCallback(this, null); } } } })); }
We now can implement the click event handler for the “Authenticate through Background” button.
private void buttonAuthenticateThroughBackground_Click(object sender, RoutedEventArgs { Config.AuthenticateThroughBackgroundTask = true; if (!HasRegisteredBackgroundTaskHandler) { foreach (var cur in BackgroundTaskRegistration.AllTasks) { if (cur.Value.Name == BackgroundTaskName) { cur.Value.Completed += new BackgroundTaskCompletedEventHandler (OnBackgroundTaskCompleted); HasRegisteredBackgroundTaskHandler = true; break; } } } return; }
Finally, we implement the methods for the activities for the Foreground app that attempt authentication.
private void buttonAuthenticate_Click(object sender, RoutedEventArgs e) { buttonAuthenticate.IsEnabled = false; // For Windows phone, we just skip authentication // because native WISPr is not supported. // Here you can implement custom authentication. authenticationContext.SkipAuthentication(); textBlockStatus.Text = "Authentication skipped"; buttonAuthenticate.IsEnabled = true; } private void checkBoxForegroundApp_Checked(object sender, RoutedEventArgs e) { if (checkBoxForegroundApp.IsChecked == true) { string token = Config.AuthenticationToken; if (token == "") { return; // no token found } if (!HotspotAuthenticationContext. TryGetAuthenticationContext (token, out authenticationContext)) { textBlockStatus.Text = "TryGetAuthenticationContext failed"; return; } buttonAuthenticate.IsEnabled = true; buttonSkip.IsEnabled = true; buttonAbort.IsEnabled = true; } } private void buttonSkip_Click(object sender, RoutedEventArgs e) { authenticationContext.SkipAuthentication(); textBlockStatus.Text = "Authentication skipped"; Config.AuthenticationToken = ""; buttonAuthenticate.IsEnabled = false; buttonSkip.IsEnabled = false; buttonAbort.IsEnabled = false; } private void buttonAbort_Click(object sender, RoutedEventArgs e) { authenticationContext.AbortAuthentication(false); textBlockStatus.Text = "Authentication aborted"; buttonAuthenticate.IsEnabled = false; buttonSkip.IsEnabled = false; buttonAbort.IsEnabled = false; }
Our application is now code-ready. However, it will not work because it needs to have certain data from mobile operators. Here are the steps once we have the data.
- Modify the Provisioning Metadata XML file to match your hotspot.
Open provisioningData.xml and modify the following fields: - CarrierProvisioning\Global\CarrierID
- CarrierProvisioning\Global\SubscriberID: This is the IMSI (International Mobile Subscriber Identity)
- CarrierProvisioning\WLANProfiles\ WLANProfile\name: Name of your service point
- CarrierProvisioning\WLANProfiles\ WLANProfile\SSIDConfig\SSID\name: Configured SSID of your hotspot
- CarrierProvisioning\WLANProfiles\ WLANProfile\MSM\security\HotspotProfile\ExtAuth\ExtensionId: Available from Visual Studio App manifest
- Authenticate access to the Provisioning Agent APIs:
- Open a PowerShell console.
- Type the following command to import the Provisioning tools:
Import-Module "<path_to_Win8_sdk>\bin\<arch>\ ProvisioningTestHelper.psd1"
- Create and install a test Extended Validation certificate:
Install-TestEVCert -CertName <Certificate Name>
- Use the new certificate to sign the provisioning file:
ConvertTo-SignedXml -InputFile <complete path to the input XML file> -OutputFile <complete path to the output XML file> -CertName <name of the certificate used to sign the xml>
- Replace ProvisioningData.xml with the signed file.
- Provide crendentials for your test access point by updating the information in Config.cs if you have hard-coded values.
Summary
In this article, we learned about building a Windows Phone app that uses Hotspot Authentication APIs. I hope you have found this information useful. You can find more information at https://msdn.microsoft.com/en-us/library/windows/apps/br212049.aspx.
About the Author
Vipul Patel is a technology geek based in Seattle. He can be reached at vipul.patel@hotmail.com. You can visit his LinkedIn profile at https://www.linkedin.com/pub/vipul-patel/6/675/508.