Monday, 24 February 2014

Windows Azure Mobile Services - Web API - Push Notifications

Node.js services have the option of manually managing device push channels (through your own channel registration table) and directly pushing to a devices URI handle or using the Notifications hub to take care of the devices for you (I notice that the Node.js services still have this, but also have an 'ENABLE ENHANCED PUSH' button in the portal to automatically integrate a notifications hub).

Web API services don't have the option to make direct platform-specific push requests and by default come with a Notification Hub instance created and ready to use.

Last year I wrote an article about using the Notifications Hub in Node.js. The implementation is very similar in Web API. This article explains how to integrate the hub into your applications.

Here is an implementation of a scheduled job which is used to create a level board for a fictitious game, it calls two methods which send toast and tile notifications for Windows Phone MPNS via the hub:

using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.WindowsAzure.Mobile.Service;
using System;

namespace TileTapperWebAPIService.ScheduledJobs
{
    public class LevelJob : ScheduledJob
    {
        public override async Task ExecuteAsync()
        {
            // Level name
            string levelName = string.Format("Daily Level {0}", DateTime.Now.ToShortDateString());

            // Logic to create level ommited
            //
            //

            string title = "New Level!";

            await this.SendToastMpns(title, levelName);
            await this.SendTileMpns(title, levelName);

            Services.Log.Info(string.Format("{0} - Created", levelName));
        }

        private async Task SendToastMpns(string text1, string text2)
        {
            try
            {
                var toast = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<wp:Notification xmlns:wp=\"WPNotification\">" +
                    "<wp:Toast>" +
                        "<wp:Text1>" + text1 + "</wp:Text1>" +
                        "<wp:Text2>" + text2 + "</wp:Text2>" +
                    "</wp:Toast> " +
                "</wp:Notification>";

                await base.Services.Push.HubClient.SendMpnsNativeNotificationAsync(toast);
            }
            catch (Exception ex)
            {
                base.Services.Log.Error(ex);
            }
        }

        private async Task SendTileMpns(string backTitle, string backContent)
        {
            try
            {
                var tile = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<wp:Notification xmlns:wp=\"WPNotification\" Version=\"2.0\">" +
                    "<wp:Tile Template=\"FlipTile\">" +
                        "<wp:BackTitle>" + backTitle + "</wp:BackTitle>" +
                        "<wp:BackContent>" + backContent + "</wp:BackContent>" +
                        "<wp:WideBackContent>" + backContent + "</wp:WideBackContent>" +
                    "</wp:Tile> " +
                "</wp:Notification>";

                await base.Services.Push.HubClient.SendMpnsNativeNotificationAsync(tile);
            }
            catch (Exception ex)
            {
                base.Services.Log.Error(ex);
            }
        }
    }

}

We can configure this in the 'SCHEDULER' tab of the portal and run on a timed schedule or on demand, or make an HTTP POST request to call it like this:

Request:
POST https://tiletapperwebapi.azure-mobile.net/jobs/level HTTP/1.1
x-zumo-master: XXXXXXXXXXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Host: tiletapperwebapi.azure-mobile.net
Content-Length: 0

Response:
HTTP/1.1 200 OK
Content-Length: 0
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=dd8538851a2df7249d42a39ee57c8edadae2824386ae2790373823ba2f34a746;Path=/;Domain=tiletapperwebapi.azure-mobile.net
Set-Cookie: WAWebSiteSID=71043e6c932a454d8c4cf9d17a9ed735; Path=/; HttpOnly
Date: Mon, 24 Feb 2014 10:59:04 GMT


Sunday, 23 February 2014

Windows Azure Mobile Services - Web API - Security

If we continue examining the template project we downloaded in this article, we notice there is no mention of security in the controller and no way of configuring permissions in the portal as there are for a Node.js service; so is our service secure and how do we adjust permissions for each controller method so we have the same level of control as in Node.js?


Default Permissions

To do a quick test and see if we have any security, we can quickly fire up fiddler and compose a simple GET request on the TodoItem, without even having to do any client-side coding:

Request:
GET https://tiletapperwebapi.azure-mobile.net/tables/todoitem HTTP/1.1
Host: tiletapperwebapi.azure-mobile.net

Response:
HTTP/1.1 403 Forbidden
Content-Length: 0
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=d3b0dd468eddabcdcdade2ddbc3126e837df5031a73d3c0f5da9eda3a33b354e;Path=/;Domain=tiletapperwebapi.azure-mobile.net
Set-Cookie: WAWebSiteSID=7f59130dd78d4d8b99c890c1868d4dac; Path=/; HttpOnly
Date: Sat, 22 Feb 2014 18:36:44 GMT

You can see we get a 403 Forbidden which means we don't have access to the API and the service is secure.

If we put the app key in the header (get this from 'MANAGE KEYS' in the portal root toolbar), we are granted access get the result we want:

Request:
GET https://tiletapperwebapi.azure-mobile.net/tables/todoitem HTTP/1.1
Host: tiletapperwebapi.azure-mobile.net
X-ZUMO-APPLICATION: XXXXXXXXXXXXXxxxxxxxxxxxxxxxxxx

Response:
HTTP/1.1 200 OK
Content-Length: 98
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=d3b0dd468eddabcdcdade2ddbc3126e837df5031a73d3c0f5da9eda3a33b354e;Path=/;Domain=tiletapperwebapi.azure-mobile.net
Set-Cookie: WAWebSiteSID=88d016736ed8442f81ff998d1db4d832; Path=/; HttpOnly
Date: Sat, 22 Feb 2014 18:48:34 GMT

[{"id":"1","complete":false,"text":"First item"},{"id":"2","complete":false,"text":"Second item"}]

This means with the default controller configuration we are secured with the application key which equates to the 'Anybody with the application key' permission in a Node.js service.

Modifying Permissions

If we want to modify the table method permissions which we are likely to do, it's not obvious what to do from the template application. I had a dig about in the 'Microsoft.WindowsAzure.Mobile.Service.Security' namespace and found that there is an attribute called 'RequiresAuthorizationAttribute' with a nice description of it's function:

"Apply this attribute to System.Web.Http.ApiController actions or controllers access to them. Based on the Microsoft.WindowsAzure.Mobile.Service.Security.AuthorizationLevel specified, access to the target action will be restricted to requests that have been granted that level or higher."

We can apply this attribute to our controller methods to achieve 4 different permissions:

Admin

This is equivalent to 'Only scripts and admins' in Node.js services and only permits requests bearing the master key (Z-ZUMO-MASTER header) or direct access from other scripts:

[RequiresAuthorization(AuthorizationLevel.Admin)]
public IQueryable<TodoItem> GetAllTodoItems()
{
    return Query();

}

Anonymous

This is equivalent to 'Everyone' in Node.js services and permits anybody on the internet to access the method which is to be used with caution:

[RequiresAuthorization(AuthorizationLevel.Anonymous)]
public IQueryable<TodoItem> GetAllTodoItems()
{
    return Query();
}

If we make a request with no auth header now we get our data.

Application

This is equivalent to 'Anybody with the application key' in Node.js services and only permits requests bearing the application key and is the same as the default with not attribute used:

[RequiresAuthorization(AuthorizationLevel.Application)]
public IQueryable<TodoItem> GetAllTodoItems()
{
    return Query();
}

User

This is equivalent to 'Only authenticated users' in Node.js services and only permits users bearing a valid OAuth2 token from one of the 4 available OAuth providers:

[RequiresAuthorization(AuthorizationLevel.User)]
public IQueryable<TodoItem> GetAllTodoItems()
{
    return Query();
}

Finally

Now we know how to set our permissions it's important to think about them for each method (if all methods require the same permissions, we can apply a single attribute to the controller). In my book I wrote a whole chapter on security and basically advised applying Admin permissions to anything which was not used by the application, then User for most other cases. Anonymous is one to be careful of as it allows access to anyone on the internet who knows the service URI.

Saturday, 22 February 2014

Publishing a Windows Azure Mobile Service Web API Project

In my previous article, we started looking at what's in the template Azure Mobile Service solution. This is a quick walk-through how to publish the solution:

1. From the dashboard in the Mobile Service portal, click the 'Download publish profile' link on the dashboard to download the services publish profile

2. From the 'Build' menu in Visual Studio, select 'Publish':




3. Click on the 'Import' button:




4. Browse to the downloaded profile and click OK:




5. Check the connection details and click 'Publish':



6. The solution should publish to our service. Check the output window for any problems.

Next we'll start looking at security in the controllers...

Tuesday, 11 February 2014

Listing Windows Azure Mobile Services NPM Packages

It's useful to know what NPM packages are pre-installed and available to use out-of-the-box in our Mobile Services. I thought it might be good to get a list of these packages and decided to create an API method which listed the packages and dependencies, with a description of what they are.

NPM list Command

The list (or ls for short) command lists the packages and can be called using the child_process.exec command. This function does this and starts examining the packages:

function npmls() {
        var exec = require('child_process').exec;
   
        // In node you can use --long switch to show description but it causes maxBuffer exceeded on parse
        var child = exec('npm ls --json',
          function (error, stdout, stderr) {
           
            var tree = JSON.parse(stdout);
            npminfo(tree, '');
           
            setNpmHttp(dequeuePackage);

            if (error !== null) {
              console.log('exec error: ' + error);
            }
        });
    }

The JSON looks like this if we log it:

{
  "problems": [
    "invalid: msnodesql@0.2.1 D:\\home\\site\\wwwroot\\node_modules\\sqlserver"
  ],
  "dependencies": {
    "apn": {
      "version": "1.3.8",
      "dependencies": {
        "q": {
          "version": "0.9.6"
        }
      }
    },
    "azure": {
      "version": "0.6.7-zumo",
      "from": "https://github.com/WindowsAzure/azure-sdk-for-node/tarball/v0.6.7-zumo",
      "dependencies": {
        "azure": {
          "version": "0.7.15",
          "from": "git://github.com/WindowsAzure/azure-sdk-for-node.git#v0.7.15-August2013",
          "dependencies": {
            "xml2js": {
              "version": "0.4.0",
              "dependencies": {
                "sax": {
                  "version": "0.5.5"
                }
              }
            },
            "request": {
              "version": "2.25.0",
              "dependencies": {
                "qs": {
                  "version": "0.6.5"
                },
                "json-stringify-safe": {
                  "version": "5.0.0"
                },
                "forever-agent": {
                  "version": "0.5.0"
                },
                "tunnel-agent": {
                  "version": "0.3.0"
                },
                "http-signature": {
                  "version": "0.10.0",
                  "dependencies": {
                    "assert-plus": {
                      "version": "0.1.2"
                    },

HTTPS Certificate Errors

This method was needed to make the npm view GET use http instead of https due to certificate trust issues:

// Used http instead of https to stop certificate errors
    function setNpmHttp (callback) {
       
        var exec = require('child_process').exec;
   
            var child = exec('npm config set registry="http://registry.npmjs.org/"',
              function (error, stdout, stderr) {
                callback();
            });      
    }

NPM Info

Once we've parsed the JSON, we can start examining each package and it's dependencies. This function walks the dependencies and builds an array of objects used for processing the package information:

// Recursive function for getting package info for all dependencies
    function npminfo(node, depth) {
         
        var dp = node.dependencies;
        if(dp !== null) {      
            for (var key in dp) {
              if (dp.hasOwnProperty(key)) {                      
               
                // Build info
                var link = 'https://www.npmjs.org/package/' + key;
                var info = depth + '<a href=\'' + link + '\', \'' + key + '\' onclick="window.open(this.href, \'' + key + '\' ); return false" target="_blank">' + key + '</a>' + ' V' + dp[key].version;
               
                packages.push({ 'key' : key, 'info' : info, 'depth' : depth.length })
                               
                // Loop through dependencies
                npminfo(dp[key], depth + '&#45;');
              }
            }
        }
    }

Extra Information

Once we have a list of package objects, we can process them to get extra description information:

function dequeuePackage(){
        // Dequeue and execute
        if(packages.length > 0){
            var pack = packages.shift();
            if(pack.depth == 0){
               
                writeLine();
               
                // Get detail
                var exec = require('child_process').exec;
       
                var child = exec('npm view ' + pack.key + ' description',
                  function (error, stdout, stderr) {
                     
                    if (error !== null) {
                      console.error('exec error: ' + error);
                    }
                    else{          
                        pack.info = pack.info + ' - ' + stdout;                        
                        writeLine('<b>' + pack.info + '</b>');
                    }
                   
                    // Do next
                    dequeuePackage();
                });
            }
            else {
                // Do next
                writeLine(pack.info);
                dequeuePackage();
            }
        }
        else {      
            // We're finished      
            opText = opText + '</body></html>';
           
            response.send(statusCodes.OK, opText);
        }  
    }

This is only done for top-level packages because it take's too long to make requests for all packages and the API request will time-out!

Full Script

When we put it all together, the API method looks like this:

exports.get = function(request, response) {
   
    // Array of package info objects
    var packages = [];
   
    // html start
    var opText = '<!DOCTYPE html><html><head><title>NPM Modules</title></head><body>';
    opText += '<h1>Windows Azure Mobile Services</h1>';
    opText += '<h2>NPM Packages and Dependencies</h2>';
   
    // List packages
    npmls();
   
    // Adds a line with break
    function writeLine(text){
        if(text)
            opText += text;
        opText += '<br />';
    }
   
    function npmls() {
        var exec = require('child_process').exec;
   
        // In node you can use --long switch to show description but it causes maxBuffer exceeded on parse
        var child = exec('npm ls --json',
          function (error, stdout, stderr) {
           
            var tree = JSON.parse(stdout);
            console.log(stdout);
            npminfo(tree, '');
           
            setNpmHttp(dequeuePackage);

            if (error !== null) {
              console.log('exec error: ' + error);
            }
        });
    }
   
    // Used http instead of https to stop certificate errors
    function setNpmHttp (callback) {
       
        var exec = require('child_process').exec;
   
            var child = exec('npm config set registry="http://registry.npmjs.org/"',
              function (error, stdout, stderr) {
                callback();
            });
       
    }
   
    // Recursive function for getting package info for all dependencies
    function npminfo(node, depth) {
         
        var dp = node.dependencies;
        if(dp !== null) {      
            for (var key in dp) {
              if (dp.hasOwnProperty(key)) {                      
               
                // Build info
                var link = 'https://www.npmjs.org/package/' + key;
                var info = depth + '<a href=\'' + link + '\', \'' + key + '\' onclick="window.open(this.href, \'' + key + '\' ); return false" target="_blank">' + key + '</a>' + ' V' + dp[key].version;
               
                packages.push({ 'key' : key, 'info' : info, 'depth' : depth.length })
                               
                // Loop through dependencies
                npminfo(dp[key], depth + '&#45;');
              }
            }
        }
    }
   
    function dequeuePackage(){
        // Dequeue and execute
        if(packages.length > 0){
            var pack = packages.shift();
            if(pack.depth == 0){
               
                writeLine();
               
                // Get detail
                var exec = require('child_process').exec;
       
                var child = exec('npm view ' + pack.key + ' description',
                  function (error, stdout, stderr) {
                     
                    if (error !== null) {
                      console.error('exec error: ' + error);
                    }
                    else{          
                        pack.info = pack.info + ' - ' + stdout;                        
                        writeLine('<b>' + pack.info + '</b>');
                    }
                   
                    // Do next
                    dequeuePackage();
                });
            }
            else {
                // Do next
                writeLine(pack.info);
                dequeuePackage();
            }
        }
        else {      
            // We're finished      
            opText = opText + '</body></html>';
           
            response.send(statusCodes.OK, opText);
        }  
    }
};

Finally

You can test it out here (although I may take it off-line at some point!).

Friday, 17 January 2014

Learning Windows Azure Mobile Services for Windows 8 and Windows Phone 8

I've spent the last few months writing a book for Packt Publishing titled Learning Windows Azure Mobile Services for Windows 8 and Windows Phone 8. The book has been published today and is available as an eBook or paper-back.

The book covers the following with examples and full solutions for Windows 8 and Windows Phone 8:

Chapter 1: Preparing the Windows Azure Mobile Services Portal

  • Choosing a subscription
  • Pay-as-you-go subscription
  • Basic and Standard subscriptions
  • Free trial
  • Creating a Windows Azure account
  • Creating a mobile service
  • Mobile Services features
  • Managing keys
  • Mobile service dashboard
  • Configure
  • Scale
  • Logs
  • Summary

Chapter 2: Start Developing with Windows Azure Mobile Services

  • Preparing our development environment
  • Hardware requirements
  • Setting up the software
  • Requirement for store accounts
  • Creating apps from the portal
  • Connecting existing apps to Windows Azure Mobile Services
  • Adding a Connected Service in Visual Studio 2013
  • Manually installing the SDK in Visual Studio 2012 Express for Windows Phone
  • Creating a table
  • Writing a model of the table
  • Interacting with the table
  • Summary

Chapter 3: Securing Data and Protecting the User

  • Configuring permissions
  • Rules for choosing permissions
  • Authentication providers
  • Authentication
  • Registering for Windows Live Connect Single Sign-on
  • Authentication in the app
  • Logging in
  • Storing credentials
  • Logging out
  • The DataServiceBase class
  • REST API and the master key
  • Summary

Chapter 4: Service Customization with Scripts

  • Understanding table scripts
  • Level-insert table script example
  • Score-insert script example
  • Score-read script example
  • API scripts
  • Creating an API script
  • High-score API script
  • Script debugging and logs
  • Scheduling
  • Working locally with Git
  • Pulling the repository
  • Updating our repository
  • Adding scripts manually
  • Pushing back changes
  • Implementing NPM modules
  • Summary

Chapter 5: Implementing Push Notifications

  • Understanding Push Notification Service flow
  • Setting up Windows Store apps
  • Setting up tiles
  • Setting up badges
  • Setting up Windows Phone 8 apps
  • Service scripts
  • WNS scripts for Store apps
  • Sending toast notifications
  • Sending tile notifications
  • Sending multiple tiles
  • Sending badge notifications
  • MPNS scripts for Windows Phone apps
  • Sending toast notifications
  • Sending tile notifications
  • Summary

Chapter 6: Scaling Up with the Notifications Hub

  • Configuring the Hub
  • Setting up Windows Store and Windows Phone 8 apps
  • Calling the hub from scripts
  • Creating WNS scripts (for Store apps)
  • Sending toast notifications
  • Sending tile notifications
  • Sending badge notifications
  • Creating MPNS scripts (for Windows Phone 8 apps)
  • Sending toast notifications
  • Sending tile notifications
  • Backend services
  • Targeting audience using tags
  • Summary

Chapter 7: Best Practices for Web-connected Apps

  • App certification requirements for the Windows Store
  • UX guidelines
  • Implementing a privacy policy
  • Checking the network connection
  • Managing notifications settings
  • Implementing settings pages
  • Summary

The content is based around a case study of a simple XAML based game to help provide real-world examples and implement as much Windows Azure Mobile Services functionality as possible with a tangible context. There is a full table of content on the book's web page.

Writing this book was an enjoyable, but difficult challenge. The main challenge was covering a large subject matter within a strict and short page limit; I chose to use a case study to help me think up realistic examples, but also provide fully working Windows 8 and Windows Phone 8 solutions to enhance the book.

There is a sample chapter available to download here (I'm fairly sure this should have been the 3rd chapter on security, but it looks to be the first one) and an article on Push Notifications here.

Anyway, if it looks of interest, please get yourself a copy!

Thursday, 28 November 2013

Creating Azure Notifications Hub Push Notifications using Node Script

Windows Azure Mobile Services has great built-in support for making Push Notifications; however there’s a better more scalable option using the Notifications Hub which is part of the Service Bus group of services.
We can call the hub from external backend services using the Windows Azure Service Bus SDK (NuGet PM> Install-Package WindowsAzure.ServiceBus) and this is fairly well documented; however if you want to do a direct replacement for the build-in Push Notifications using Notifications Hub, it’s not so well documented. I did some digging in the source to work out how to use it: https://github.com/WindowsAzure/azure-sdk-for-node
In our Mobile Services scripts we can make use of NPM packages which we install ourselves and pre-installed packages like the Windows Azure SDK for Node which we will use for calling the Hub.

Accessing the Notifications Hub

We need two things to allow us to connect to the hub and those are the DefaultFullSharedAccessSignature which you can get from the Notifications Hub Dashboard and the Hub name. We can declare these as variables like this:

var CONNECTION_STRING = "Endpoint=sb://myapp.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=XXXXXXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=";
var HUB_NAME = "MyHubName";

Now to interact with the Notifications hub, we get a reference to the Azure package like this:

var azure = require("azure");

Then create a NotificationHubService like this:

var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);

All the native PNS send methods have prototypes similar to this:

MpnsService.prototype.send = function (tags, payload, targetName, notificationClass, optionsOrCallback, callback)

The Notifications class is the batching interval (how quick it’s sent).

Windows Phone MPNS Notofications

Following are examples of sending toast and tiles to Windows Phone apps using the MPNS provider:   

function sendToastHubMpns(text1, text2, tagExpression)
{
    var azure = require("azure");
    var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);
   
    var toast = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
    "<wp:Notification xmlns:wp=\"WPNotification\">" +
        "<wp:Toast>" +
            "<wp:Text1>" + text1 + "</wp:Text1>" +
            "<wp:Text2>" + text2 + "</wp:Text2>" +
        "</wp:Toast> " +
    "</wp:Notification>";
   
    notificationHubService.mpns.send(tagExpression, toast, "toast", 2, function(error) {
    if (error) {
        console.error(error);
    }});
}

function sendTileHubMpns(backTitle, backContent, tagExpression)
{
  var azure = require("azure");
  var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);
   
  var tile = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
      "<wp:Notification xmlns:wp=\"WPNotification\" Version=\"2.0\">" +
         "<wp:Tile Template=\"FlipTile\">" +
              "<wp:BackTitle>" + backTitle + "</wp:BackTitle>" +
              "<wp:BackContent>" + backContent + "</wp:BackContent>" +
              "<wp:WideBackContent>" + backContent + "</wp:WideBackContent>" +
         "</wp:Tile> " +
      "</wp:Notification>";
 
    notificationHubService.mpns.send(tagExpression, tile, "token", 1, function(error) {
    if (error) {
        console.error(error);
    }});
}

Windows 8 WNS Notifications

Following are examples of sending toast, tile and badge notifications to Windows 8 apps using the WNS provider:   

function sendToastHubWns(text1, text2, text3, tagExpression)
{
  var azure = require("azure");
  var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);
 
  var toast = "<toast>" +
    "<visual>" +
      "<binding template=\"ToastText04\">" +
        "<text id=\"1\">" + text1 + "</text>" +
        "<text id=\"2\">" + text2 + "</text>" +
        "<text id=\"3\">" + text3 + "</text>" +
        "</binding>" +
      "</visual>" +
    "</toast>";

    notificationHubService.wns.send(tagExpression, toast, "wns/toast", 2, function(error) {
    if (error) {
        console.error(error);
    }});
}

function sendTileHubWns(text1, text2, text3, tagExpression)
{
    var azure = require("azure");
    var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);
   
    var tile = "<tile>" +
    "<visual>" +
      "<binding template=\"TileSquareText01\">" +
        "<text id=\"1\">" + text1 + "</text>" +
        "<text id=\"2\">" + text2 + "</text>" +
        "<text id=\"3\">" + text3 + "</text>" +
      "</binding>" +
    "</visual>" +
    "</tile>";
 
    notificationHubService.wns.send(tagExpression, tile, "wns/tile", 2, function(error) {
    if (error) {
        console.error(error);
    }});
}

function sendBadgeHubWns(value, tagExpression)
{
    var azure = require("azure");
    var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);
   
    var badge = "<badge value=\"" + value + "\" />";
 
    notificationHubService.wns.send(tagExpression, badge, "wns/badge", 2, function(error) {
    if (error) {
        console.error(error);
    }});
}

These functions can be called like this:

function sendAllHubNotifications(message)
{
  sendToastHubMpns("My Application", message, null);
  sendTileHubMpns("My Application ", message, null);

  sendToastHubWns("My Application ", "Here’s a notification", message, null);
  sendTileHubWns("My Application ", " Here’s a notification ", message, null;
  sendBadgeHubWns("alert",  null);
}

Notice there is a tagExpression variable which we have set to null. We can use this to target notifications at user’s interests which are registered in the app:

function sendAllHubNotifications(message)
{
  sendToastHubWns("My Application ", "Here’s a notification", message, "NEWS");
  sendTileHubWns("My Application ", " Here’s a notification ", message, "NEWS");
  sendBadgeHubWns("alert", "NEWS");
}

I managed to get Windows 8 to work with this but not Windows Phone 8

Registering with the Hub from a Windows 8 App

This example shows how to register a Windows 8 Store App with the DefaultListenSharedAccessSignature connection string this time (from the Notifocations Hub Dashboard). This registers two tags, but you can use null to receive everything:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.MobileServices;
using Newtonsoft.Json.Linq;
using Microsoft.WindowsAzure.Messaging;

namespace MyApp
{
    internal class MyAppPush
    {
        private const string HUB_NAME = "myapp";
        private const string CONNECTION_STRING = "Endpoint=sb://myapp.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=/XXXXXXXXXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=";

        public async static void UploadChannel()
        {
            var channel = await Windows.Networking.PushNotifications.PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

            var token = Windows.System.Profile.HardwareIdentification.GetPackageSpecificToken(null);
            string installationId = Windows.Security.Cryptography.CryptographicBuffer.EncodeToBase64String(token.Id);

            try
            {
                // Register with hub
                var hub = new NotificationHub(HUB_NAME, CONNECTION_STRING);
                var result = await hub.RegisterNativeAsync(channel.Uri, new string[] { "NEWS", "SUPPORT" });
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }
        }
    }
}


Windows Phone 8 apps are registered in the same way, but they have a slightly different mechanism as they have an event when the URI changes which needs to be used for registration.