Organized Architecture for a Cross-Platform SignalR Application

SignalR is a great tool for keeping not only your web applications synced in real-time, but with it’s easy to use APIs for clients, it is perfect for use in your mobile applications.

I have some previous posts that are going to help in designing the of an architecture for a cross-platform implementation with SignalR. Take a look at these:

In this scenario, we are going to focus on two major layers of separation within our applications, although you can always add more layers of abstraction if that is your style or preference. Our two layers are the Portable Class Library and Native Client Libraries. This example is going to be using one PCL with all of our shared code, and then individual projects for Windows Phone 8.1, Windows 8.1, Xamarin.iOS, and Xamarin.Android.

The Portable Class Library

In this architecture, we want to focus on sharing as much code as possible. This implies that the only code that should not be in our PCL is code that includes the UI (Note we are not using Xamarin.Forms), updates the UI, or accesses the device’s native features (cameras, location, etc). This would include things such as ViewModels if you’re using the MVVM pattern (this example is using MVVM Light), Models, Web Request Logic, and even our SignalR Managers.
With that in mind, here are some nuget packages to consider for your PCL:

So let’s get started with a simple BaseHubManager to manage our connections. I’m going to follow a basic Inheritance pattern for these managers so that we can share as much of our SignalR code in our Base Manager as possible.

BaseHubManager:

  public class BaseHubManager
    {
        public HubConnection Connection{ get; set; }
        public IHubProxy Proxy { get; set; }

        // The empty constructor should only be used for a basic connection with no specific Hub
        public BaseHubManager()
        {
            Connection= new HubConnection(Constants.BaseUrl);
        }

        // Connect to specific Hub
        public BaseHubManager(string hubProxy)
        {
            Connection= new HubConnection(Constants.BaseUrl);
            Connection.Headers.Add("Authorization", string.Format("Bearer {0}", App.CurrentUser.TokenInfo.AccessToken)); //add access token to authorize
            Proxy = Connection.CreateHubProxy(hubProxy);
        }

        public async Task Start()
        {
            await _connection.Start(new LongPollingTransport());
            //Add additional shared Start logic
        }

        public void Stop()
        {
             _connection.Stop();
             //Add additional shared Stop logic
        }

        public virtual async void Connect()
        {
            await this.Start();
            //Add additional shared Connect logic
        }
    }

Now that we have a solid base, we can easily spin up individual Hub managers to connect to specific Hubs. Below is an example of a HubManager that would connect to the “ChatHub” on your server.

ChatHubManager:

 public class ChatHubManager : BaseHubManager
    {
        public ObservableCollection<string> Messages{ get; set; }

        public ChatHubManager ()
            : base("ChatHub")
        {
            this.Connect();
        }
        // Send new message to ChatHub
        public async Task<string> Create(string newMessage)
        {
            //establish connection
            await this.Start();

            var message= await Proxy.Invoke<string>("Create", newMessage);

            return message;
        }

        // Get all messages from ChatHub
        public async Task<IEnumerable<string>> Get()
        {
            //establish connection
            await this.Start();

            var messages = await Proxy.Invoke<IEnumerable<string>>("Get"));

            Messages = new ObservableCollection<string>(messages);
            return messages;

        }

    }

Expanding new HubManagers like this one make it very easy to call server-side methods from our client by simply creating an instance of our Manager and calling our Invoking methods:

var manager = new ChatManager();
var messages = await manager.Get();

Now what about the other side of SignalR – Client methods? There are some things to consider. Most importantly, will your client end points update anything in the UI? This also includes updating ViewModel properties that will cause updates in the UI.
The problem is that the client end point listeners don’t stem from any sort of UI action the way that calling a server method might. Thus, it can’t run on the UI thread by itself and you would likely run into some sort of Threading Exception.

If you don’t need to update the UI at any point from your client listener, consider adding your listeners in your manager with something like this:

public ChatHubManager ()
    : base("ChatHub")
{
        this.Connect();
        Proxy.On<string>("newMessage", async data =>
        {
            //Do something that is NOT on the UI thread
        }
}

One thing that SignalR does wonderfully is give you the ability to add these listeners from anywhere. That includes from your individual client projects that reference your PCL. So, let’s look at how to add these kind of requests to our client projects so we can update our UIs.

The Individual Client Projects

Let’s use a Windows Phone 8.1 project as our example, although the same logic can be applied to any type of project. So we need to add our client listener like we did above, but we also need it to make changes to our UI, which will require tapping into our UI thread. For this case, let’s add a ChatSignalingManager to our Windows Phone project that contains a reference to our ChatHubManager in our PCL above:

 public class ChatSignalingManager
    {
        private ChatHubManager _hubManager;
        private CoreDispatcher _dispatcher;
        public ChatSignalingManager (CoreDispatcher dispatcher)
        {
            _hubManager= new ChatHubManager ();
            _dispatcher = dispatcher;
            InitializeChatEndpoints();
        }

       //Add client end points for the ChatHubManager
        private void InitializeChatEndpoints()
        {
            var newMessage = _hubManager.Proxy.On<string>("newMessage", async data =>
            {
                await _dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        // process your UI changes here
                    });
            });

           // Add additional end points for the ChatHub here
        }

    }

Notice the biggest difference in how we handle our end point in our client project versus our PCL. The Dispatcher. In this case, we can pass Windows CoreDispatcher to our constructor, and by calling _dispatcher.RunAsync we are able to execute our process on our UI thread to successfully run code against our UI.

Here is an example of constructing the ChatSignalingManager from the MainPage of a Windows Phone app:

public sealed partial class MainPage : Page
{

    private ChatSignalingManager _signalingManager;
    public MainPage()
    {
        this.InitializeComponent();

        this.NavigationCacheMode = NavigationCacheMode.Required;
        _signalingManager = new ChatSignalingManager (Dispatcher);

    }

}

Hopefully that is enough code to get you started, but feel free to ask more questions in the comments. Just remember to share as much code as possible in your PCL and use your Client Projects to handle updates to your UI and also to utilize SignalR’s flexibility and versatility.

Advertisements

Connecting to an Authorized SignalR Hub from a .NET Client

In a previous post, I talked about adding Access and Refresh tokens to your Web Application using OAuth Bearer Tokens. In this post, we are going to be using this same logic to authorize external clients from an external .NET client application such as Windows Store apps, Xamarin.iOS, Xamarin.Android, etc.

Assuming we have our access token (and refresh token) stored locally on our client, we can use it to authorize our requests to our SignalR Hub. Let’s put together a basic Hub:

[Authorize]
public class SimpleHub : Hub
{
    public string AuthorizedString()
    {
        return "You are successfully Authorized";
    }

}

This is obviously and extremely simple example, and we aren’t going to get into calling client methods from the server with our authorized user as I will be covering that in a later post.
Now that we have our server-side Hub, let’s put together a client-side manager to connect to this Hub and make our request to AuthorizedString()

public class SimpleHubManager
{
    private HubConnection _connection;
    private IHubProxy _proxy;
    public SimpleHubManager()
    {
        _connection = new HubConnection("http://YOUR_DOMAIN/"); //connect to SignalR on your server
        _connection.Headers.Add("Authorization", string.format("Bearer {0}", YOUR_ACCESS_TOKEN)); //THIS IS WHERE YOU ADD YOUR ACCESS TOKEN MENTIONED ABOVE
        _proxy = _connection.CreateHubProxy("SimpleHub"); //connect to Hub from above
    }

    public async Task<string> GetAuthorizedString()
    {
        await _connection.Start(); //start connection
        var authorizedString = await _proxy.Invoke<string>("AuthorizedString"); //Invoke server side method and return value
        return authorizedString;
    }
}

As long as the Access Token being used by the client has not expired and is added to the Authorization Http Header, then we will be able to bypass the [Authorization] on the server.
So now, from our client, if we call:

var manager = new SimpleHubManager();
var authString = await manager.GetAuthorizedString(); //"You are successfully Authorized"

We see our string is exactly what we expect.

Stay tuned for some more advanced SignalR work in the future!

Databinding a Windows FlipView with MVVM Light

MVVM Light is a great cross-platform MVVM Framework, and was chosen to be used in this example. However, it isn’t required to get the same results.

FlipViews were a great addition to the WIndows Control family for Windows Phone and Windows Store applications. It’s easily customized, simple to use, and can give your app a user friendly experience for anything from an image gallery, to a news reader. So let’s get to the code.

Let’s first build the model we are going to use for each of our views in our FlipView. I’m using a PCL for my Models and ViewModels and a Windows Phone 8.1 project for the xaml, but it works in the same fashion for Windows Store.

Here is our model:


public class RotatorItem
 {
     public string Title { get; set; }
     public string ImageUri { get; set; }
     public string Subtitle { get; set; }
 }

Now let’s look at a simple ViewModel with MVVM Light to hold our collection of SimpleRotatorItems.

public class LandingRotatorPageViewModel : ViewModelBase
 {
     #region Private Properties
     private ObservableCollection<RotatorItem> _rotatorItems;
     private RotatorItem _activeItem;
     #endregion

     #region Public Properties
     public ObservableCollection<RotatorItem> RotatorItems
     {
         get
         {
             return _rotatorItems;
         }
         set
         {
             Set(() => RotatorItems, ref _rotatorItems, value);
         }
     }
     public RotatorItem ActiveItem
     {
         get
         {
             return _activeItem;
         }
         set
         {
             Set(() => ActiveItem, ref _activeItem, value);
         }
     }

     #endregion
     public LandingRotatorPageViewModel()
     {
         var rotatorItems = new ObservableCollection<RotatorItem>();
         rotatorItems.Add(new RotatorItem { ImageUri = "/Assets/Logo.scale-240.png", Title = "Title 1", Subtitle = "Subtitle 1"});
         rotatorItems.Add(new RotatorItem { ImageUri = "/Assets/Logo.scale-240.png", Title = "Title 2", Subtitle = "Subtitle 2" });
         rotatorItems.Add(new RotatorItem { ImageUri = "/Assets/Logo.scale-240.png", Title = "Title 3", Subtitle = "Subtitle 3" });
         rotatorItems.Add(new RotatorItem { ImageUri = "/Assets/Logo.scale-240.png", Title = "Title 4", Subtitle = "Subtitle 4" });
         RotatorItems = rotatorItems;
         ActiveItem = rotatorItems.First();
     }
 }

Let’s go ahead now and get our markup done for our page with the FlipView:

<FlipView x:Name="SimpleFlipView" ItemsSource="{Binding RotatorItems}" Grid.Row="1" SelectionChanged="FlipView_SelectionChanged">
    <FlipView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="200" />
                    <RowDefinition Height="120" />
                    <RowDefinition Height="80" />
                </Grid.RowDefinitions>
                <Image Source="{Binding ImageUri}" Height="150" Grid.Row="0" VerticalAlignment="Top" Margin="0,50,0,0"/>
                <TextBlock TextWrapping="Wrap" TextAlignment="Center" Grid.Row="1" Text="{Binding Title}" FontSize="30" HorizontalAlignment="Center" Margin="20"></TextBlock>
                <TextBlock TextWrapping="Wrap" TextAlignment="Center" Grid.Row="2" FontSize="20" HorizontalAlignment="Center" Margin="20">
                    <Run Text="{Binding Subtitle}"></Run>
               </TextBlock>

            </Grid>
       </DataTemplate>
   </FlipView.ItemTemplate>
</FlipView>

Last but not least, let’s make sure our ViewModel is bound to our Page in our page Class:

 public sealed partial class MainPage : Page
 {
     private LandingRotatorPageViewModel _vm;
     public MainPage()
     {
          this.InitializeComponent();

          _vm = new LandingRotatorPageViewModel();
          this.DataContext = _vm;
     }

     /// <summary>
     /// Invoked when this page is about to be displayed in a Frame.
     /// </summary>
     /// <param name="e">Event data that describes how this page was reached.
     /// This parameter is typically used to configure the page.</param>
     protected override void OnNavigatedTo(NavigationEventArgs e)
     {
     }
}

Now that the Page’s DataContext is set to our ViewModel, the binding we placed on the FlipView element ‘ItemSource=”{Binding RotatorItems}”‘ will bind our FlipView’s Items to the items we created in the constructor for our ViewModel, and will use our ItemTemplate to create our items from the fields on the RotatorItems.

Now let’s take a look at our final result:

FlipView Item
First FlipView Item bound to ViewModel
wp_ss_20150502_0002
Second FlipView Item

So there you have it. An easy solution to using the Windows FlipView Control to display content that is bound to a ViewModel.

Adding a Simple Refresh Token to OAuth Bearer Tokens

If you’re using a .NET as your web platform and are looking to expand it to another platform such as mobile applications, and need to authenticate users from that external application, one of the best ways of going about it is through the use of OAuth Bearer Tokens.

James Randall has a great post here about getting started with the OAuth Bearer token Authentication. This post isn’t going to focus on getting started, but will use this example to expand upon.

Using Bearer (access) Tokens allows you to authenticate users without having to send their password through the pipes with each request. Using an access token in your header will let you authorize requests to your api as well as through SignalR or other web services.

Here is an example of the authorization header sent with a request to authorize a user:
“Authorize Bearer YOUR_ACCESS_TOKEN”

However, what happens when this token expires? Of course, you can set an outrageously long expiration date, but that is a security nightmare. You don’t want to store the users password locally to continuously send requests to get a new token. You also don’t want to require the user to re-login every time the token expires.

The solution? Refresh tokens! A refresh token will allow you to receive a new access token after it expires without sending the user’s password.

The first step is to create a RefreshTokenProvider that we can add during our Startup processing. Here is a simple Provider that will work for this example:


 public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
 {
    private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();

     public async Task CreateAsync(AuthenticationTokenCreateContext context)
     {
         var guid = Guid.NewGuid().ToString();

         // maybe only create a handle the first time, then re-use for same client
         // copy properties and set the desired lifetime of refresh token
         var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
         {
             IssuedUtc = context.Ticket.Properties.IssuedUtc,
             ExpiresUtc = DateTime.UtcNow.AddYears(1)
         };
         var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);

         //_refreshTokens.TryAdd(guid, context.Ticket);
         _refreshTokens.TryAdd(guid, refreshTokenTicket);

         // consider storing only the hash of the handle
         context.SetToken(guid);
     }

     public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
     {
         AuthenticationTicket ticket;
         if (_refreshTokens.TryRemove(context.Token, out ticket))
         {
             context.SetTicket(ticket);
         }
     }

     public void Create(AuthenticationTokenCreateContext context)
     {
         throw new NotImplementedException();
     }

     public void Receive(AuthenticationTokenReceiveContext context)
     {
         throw new NotImplementedException();
     }
 }

We can now use this provider to add the setting to our Startup.Auth.cs:


 OAuthOptions = new OAuthAuthorizationServerOptions
 {
     TokenEndpointPath = new PathString("/Token"),
     Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
     AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
     AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
     AllowInsecureHttp = true,
     RefreshTokenProvider = new SimpleRefreshTokenProvider() //REFERENCE TO PROVIDER

 };

Now we have our access token that expires every hour, and a refresh token that expires every year. The time-spans to use for both of these is completely up to you.

Now we can use requests like these with our external application-

Authorize with username and password:

Method: POST
Url: http://yourdomain/Token?username=yourusername&password=yourpassword&grant_type=password

Response:

{

"access_token":"VSt0JjzP-9PO6OFk-i_dp7Xs7RA4JTai_nv1FXxTiZ-iMoYjCt42Jw8eJqV66EouqAxnsHIUzDucHKSQUhEch9tftf_dNgi0pDKFUZn5UVJ0rybZ8keG4LjT2oI851D1OnE0Ij0KnEr5ox_RNFpYW5Srqj_4Uy4uYkhrOLKxo3TEt_nBFNhVsvTAxoY5ggDdTK_th945XzeZeXjRSX-j8clYJpaxAUmA-Z38qhbyXiq29wSZKswhloaHcIVIJDXe9Fhpfe1nM4IfJT5Lwy1tjYH4XIphd7UX_nprX4JEwlJUFENJE9E-Gq6y7deXQa7j3JXIg8YBtvcR0Mj0Fjxhj6Bdaq2hCE1Ot6KgZUxOzzRkiuJlkMoQgmg8T2MM6STfQnX-cEd328n6oYgYBxg34kLbi8NGSHiAKEtxcF8Fuj7gizMOCK91iaVQTf_7UsJIkW6KFGeGLz0MG8A71jj-kNjzSFApYGo6VCoQJqXzREY",

"token_type":"bearer",

"expires_in": 3600,

"refresh_token":"969c9b04-afe5-48a3-9353-62509f71e906",

"userName":"yourusername",

".issued":"Thu, 30 Apr 2015 02:21:08 GMT",

".expires":"Thu, 30 Apr 2015 02:22:08 GMT"

}

Authorize with username and refresh token:

Method: POST
Url: http://yourdomain/Token?username=yourusername&refresh_token=969c9b04-afe5-48a3-9353-62509f71e906&grant_type=refresh_token

Response:

{

"access_token":"SAPmj6kWat4KcwhASsTkkuQ0hxeIZaq4ztZBduHV_Mr-0SoxzQZ61ojdiXDtUIo_ptfbuzx5sA9_3-GpPWZhQ702qvAXdYSnMy_OUVVypfVkP-9mUsS7iR_4uFd67MFrSVEfQ4Er1Tm9AiFLC1j4kR7WjAmZgn6YuhU1Z3NNOFMu6UGEutJEWZte4mcnHinYKxskwVt_45DBGqEaLQ1OQoPhYwLTPGIhAcvsjiVLOxHCWp46bfYOyP5tVBoZxoaftuYyQfEgOkeU44TRWdGRZBh6vKWKdjWqa-qpy8fCNoJkwpSSjWYGEyhG4IkDyRCRGpCfMHP5rbP6dfaWAAchk7qQCmcia_vuEFoZWFWER6_LFe58avh_ZqfmJQhl7lVaM4z5SEKmKP4RPXgK32T4jQEqoisOGi66bcueLzRGmCsW2BlBnxPC1QloY_VQR8bEoCqK7_C0haMH7t30sJz_2Cz9CgnMnIjeyVhdcQsg_4U",

"token_type":"bearer",

"expires_in": 3600,

"refresh_token":"9a773700-0b48-411e-9138-1fc0e266d8a9",

"userName":"yourusername",

".issued":"Thu, 30 Apr 2015 02:21:08 GMT",

".expires":"Thu, 30 Apr 2015 02:22:08 GMT"

}

This is obviously very simplified and lacks typical uses with things like Client Ids and handling proper storage and error handling, but it will hopefully help you get started with your Authorization layer for any external applications through Web API.