OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


Forms Authentication with SignalR Using Web Forms and Windows Forms

image

A Method To Provide Some Security With SignalR

SignalR is a fantastic tool for creating real-time applications. It is not just limited to making chat applications, but we will use a chat application to demonstrate how you can implement Forms Authentication with SignalR.

The reason you would want to use Forms Authentication, is that most websites use Forms Authentication now. Forms Authentication also works with all web browsers and most devices.

SignalR

According to www.ASP.net:

SignalR is an open-source .NET library for building web applications that require live user interaction or real-time data updates. Examples include social applications, multiuser games, business collaboration, and news, weather, or financial update applications. These are often called real-time applications.

You can find more about SignalR at this link: http://signalr.net/.

The Sample Application

image

When we run the LightBulbSignalRService project we see a simple SignalR application that uses Persistent Connections (rather than the Hubs that most examples use).

When the application starts, we are not logged in so our connection GUID shows with a message that we are connected.

image

If we click the Login as TestUser button, we are given a cookie and SignalR detects that we are authenticated.

SignalR also puts us in a group that has the same name as our user name.

image

We can open up another Tab, log in again as TestUser, enter a message and click the broadcast button…

image

When we return to the first Tab, it will see the Group Message because it is in the same group. This is just to demonstrate that you can authenticate a user and keep data in sync across multiple instances,  and as will be demonstrated in the next step, multiple devices.

For example, imagine a user is working with your application on a web page and then wants to pick up from where they left off on their tablet or cell phone (or any other device).

image

Now, we run the LightBulbSignalRClient project and set the connection and click the Connect button.

image

We are now logged in via a Windows Forms Application using Forms Authentication!

The Web Application

image

We will first cover the Web Application (the LightBulbSignalRService project).

The first place we want to start is the web.config file. It is there that we want to turn Forms Authentication on by setting the authentication tag to mode=”Forms”:

xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <authentication mode="Forms"/>
  system.web>
configuration>

image

Next, we use NuGet to pull in SignalR.

We create a Global.asax file and add the following code to the Application_Start:

image

This simply indicates that we will have a SignalR Persistent Connection called MyConnection that can be accessed by the end point “echo”.

To implement the MyConnection class, we use the following code in the MyConnection.cs file:

 

using System.Collections.Generic;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hosting;
public class MyConnection : PersistentConnection
{
    protected override Task OnReceived(IRequest request, string connectionId, string data)
    {
        // Broadcast to all instances of the user 
        // (that user is in a group that matches their user name)
        Groups.Send(getClientDescription(request, connectionId), "Group Message: " + data);
        // Broadcast to all clients
        return Connection.Broadcast("User: " 
            + getClientDescription(request, connectionId) + " Sent: " + data);
    }
    protected override Task OnConnected(IRequest request, string connectionId)
    {
        // Add the user to a group that matches their user name
        // If they are not logged in, their group will be their
        // Connection ID
        Groups.Add(connectionId, getClientDescription(request, connectionId));
        return Connection.Broadcast("User: " 
            + getClientDescription(request, connectionId) + " connected");
    }
    protected override Task OnReconnected(IRequest request, string connectionId)
    {
        // User Reconnected (they come back)
        return Connection.Broadcast("User: " 
            + getClientDescription(request, connectionId) + " re-connected");
    }
    protected override Task OnDisconnected(IRequest request, string connectionId)
    {
        // User Disconnected (they may come back)
        return Connection.Broadcast("User: " 
            + getClientDescription(request, connectionId) + " disconnected");
    }
    // Utility
    private static string getClientDescription(IRequest request, string connectionId)
    {
        // Note: Using HttpContext.Current is a no no with SignalR, 
        // so get the IPrincipal from the SignalR IRequest via IRequest.User
        IPrincipal objIPrincipal = request.User;
        if (objIPrincipal != null)
        {
            // If user is authenticated get user name
            // otherwise use connectionId
            var name = objIPrincipal.Identity.IsAuthenticated
                    ? objIPrincipal.Identity.Name
                    : connectionId;
            return name;
        }
        else
        {
            return connectionId;
        }
    } 
}

image

Next, we create a Default.aspx page.

 

It uses the following markup:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>SignalR Testtitle>
    <script src="Scripts/jquery-1.9.1.min.js" type="text/javascript">script>
    <script src="Scripts/json2.js" type="text/javascript">script>
    <script src="Scripts/jquery.signalR-1.0.0.min.js" type="text/javascript">script>
    <script type="text/javascript">
        $(function () {
            // For IIS deployment use: $.connection('echo');
            var connection = $.connection('/echo');
            // This is fired when a message is received
            connection.received(function (data) {
                $('#messages').append('<li>' + data + 'li>');
            });
            // Start the connection
            connection.start().done(function () {
                $("#broadcast").click(function () {
                    connection.send($('#msg').val());
                });
            });
        });
    script>
head>
<body>
    <form id="form1" runat="server">
        <div>
            <input type="text" id="msg" />
            <input type="button" id="broadcast" value="broadcast" /> 
            <asp:Button ID="btnLoginAsTest" runat="server" 
                OnClick="btnLoginAsTest_Click" Text="Login as TestUser" />
             
            <asp:Button ID="btnLogout" runat="server" 
                OnClick="btnLogout_Click" Text="Logout" />
             <ul id="messages">
            ul>
        div>
    form>
body>
html>

 

And the following code behind:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace LightBulbSignalRService
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }
        #region btnLoginAsTest_Click
        protected void btnLoginAsTest_Click(object sender, EventArgs e)
        {
            // *************************
            // Insert user validation here
            // For example, check the username and password in the database
            // *************************
            // Log the user into the site
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
              "TestUser",
              DateTime.Now,
              DateTime.Now.AddDays(30),
              false,
              "SignalR",
              FormsAuthentication.FormsCookiePath);
            // Encrypt the ticket.
            string encTicket = FormsAuthentication.Encrypt(ticket);
            // Create the cookie.
            Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
        } 
        #endregion
        #region btnLogout_Click
        protected void btnLogout_Click(object sender, EventArgs e)
        {
            FormsAuthentication.SignOut();
        } 
        #endregion
    }
}

 

To authenticate remote users, we create a file CreateCookie.aspx with the following code:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;
namespace ODataSample
{
    public partial class CreateCookie : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string strUserName = Request.QueryString["UserName"];
            string strPassword = Request.QueryString["Password"];
            // *************************
            // Insert user validation here
            // For example, check the username and password in the database
            // *************************
            // Create cookie and return it
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
                strUserName,
                DateTime.Now,
                DateTime.Now.AddDays(30),
                false,
                "SignalR",
                FormsAuthentication.FormsCookiePath);
            // Encrypt the ticket.
            string encTicket = FormsAuthentication.Encrypt(ticket);
            // Create the cookie.
            Response.Write(encTicket);
            Response.End();
        }
    }
}

The Windows Forms Client

image

The Windows Forms Client has to have the ASP.NET SignalR Client installed.

image

We use NuGet to pull in the .NET SignalR Client.

We use the following code for the Connect button:

            // Connect to the service
            connection = new Connection(txtSignalRWebsite.Text);
            // Fire connection_Received when message comes in
            connection.Received += connection_Received;
            // Create a Cookie
            Cookie objCookie = new Cookie();
            // Set the values
            objCookie.Domain = txtDomain.Text;
            objCookie.Expires = DateTime.Now.AddMinutes(20);
            objCookie.HttpOnly = false;
            objCookie.Name = ".ASPXAUTH";
            objCookie.Path = @"/";
            objCookie.Secure = false;
            objCookie.Value = GetCookie(); // The Forms Auth Ticket
            // Create a Cookie container and put the Cookie inside
            connection.CookieContainer = new CookieContainer();
            connection.CookieContainer.Add(objCookie);
            // Start the connection
            connection.Start().Wait();

 

The following is the code that connects to the CreateCookie.aspx file and gets the cookie:

 

        private string GetCookie()
        {
            WebClient client = new WebClient();
            // Add a user agent header in case the 
            // requested URI contains a query.
            client.Headers.Add("user-agent", 
                "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
            
            string strWebRequest = 
                String.Format("{0}?UserName={1}&Password={2}", 
                txtAuthWebsite.Text, txtUserName.Text, txtPassword.Text);
            Stream data = client.OpenRead(strWebRequest);
            StreamReader reader = new StreamReader(data);
            string strCookie = reader.ReadToEnd();
            data.Close();
            reader.Close();
            return strCookie;
        } 

 

SignalR Authentication

image

This method was created because this is what it appears that one of the SignalR creators, David Fowler indicated was the way it should be handled (http://stackoverflow.com/questions/11488461/forms-authentication-with-signalr).

Download Sample Code

You can download the code from this link:

http://silverlight.adefwebserver.com/Files/SignalrRWindowsFormsAuth.zip





Comments are closed.