Integrating BrowserId with ASP.NET Membership, Roles, Profile and Forms Authentication

Now that BrowserId is ready for prime time and works in all major browsers, I thought it would be a good time to illustrate how I've integrated it with Forms Authentication and the ASP.NET Membership, Role and Profile providers.

If you are not yet familiar with BrowserID, you can read up on it here: https://browserid.org/  There's a nice little Youtube video explanation, along with plenty of documentation.

As a new BrowserID user, you confirm your email one time via an emailed confirmation link. After that, you can sign in to any BrowserID-enabled site with only your verified email address. No need to store any passwords! This is more natural to users than say, OpenID. It's also a lot easier for the developer.

There are two components to implementing BrowserID in ASP.NET:

1) Some Client script that you put in your login page (or UserControl, if you like)
2) A .NET class that will take the encrypted assertion that comes back to the browser, and verify it. The most important item that this returns (server side) is the authenticated user's email address. You now know that this is a verified email, that the user is who they say they are, and so there is no need for clumsy sign-up forms or passwords to remember (or that need to be stored in a a database which subjects you to security issues).

Here is sample code for the browser script:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Login.ascx.cs" Inherits="BrowserID.Net.Login1" %>

<script src="https://browserid.org/include.js" type="text/javascript"></script>
<script type="text/javascript">
<!--
    function browserID() {
        navigator.id.getVerifiedEmail(function (assertion) {
            if (assertion) {
                document.getElementById('browserid_assertion').value = assertion;
                document.getElementById('browserid_form').submit();
            } else {
                alert('Could not complete identification');
            }
        });
        return false;
    }
-->
</script>

  <div align="center">  
<input type="hidden" name="assertion" value="" id="browserid_assertion" />
<asp:ImageButton id=imgSubmit runat=server ImageUrl="images/sign_in_blue.png" />  
<asp:Label ID=lblMessage runat="server" />
</div>

And my code for the verification class:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Web;
using Newtonsoft.Json;

namespace BrowserID.Net
{
    public class BrowserId
    {
         public string Audience { get; set; }
        public string Assertion { get; set; }
         public string Email { get; set; }
        public string Validity { get; set; }
        public string Issuer { get; set; }

         private string PostRequest(string url, NameValueCollection data)
        {
            WebClient wc = new WebClient();
            byte[] b = wc.UploadValues(url, data);
            string s = System.Text.Encoding.UTF8.GetString(b);
             return s;
        }

         public BrowserId(string audience, string assertion)
         {
             this.Audience = audience;
             this.Assertion = assertion;
        }

        /*
         Send the assertion to the browserid.org server (this must be over HTTPS)
          The response is read to determine if the assertion is authentic
         */
         public bool VerifyAssertion()
        {
            NameValueCollection parameters = new NameValueCollection();
             parameters.Add("assertion", this.Assertion);
             parameters.Add("audience", this.Audience);
    string s=PostRequest("https://browserid.org/verify", parameters);
      
            JsonData obj=   JsonConvert.DeserializeObject<JsonData >(s);
             if(  obj.status=="okay")
             {
               this.Email  =  obj.email ;
               this.Validity = obj.validity;
                 this.Issuer = obj.issuer;
               return true;
             }
             else
             {
               return false;
             }
    
         }
    }

     public class JsonData
    {
         public string email { get; set; }
        public string status { get; set; }
        public string validity { get; set; }
        public string issuer { get; set; }
    }
}

I use NewtonSoft.JSON to handle the JSON ->JsonData class deserialization

That's it! That's all it takes.  And here is the codebehind class for my UserControl:

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 BrowserID.Net
{
    public partial class Login1 : System.Web.UI.UserControl
    {
         protected void Page_Load(object sender, EventArgs e)
         {
             Page.Form.Attributes.Add("onSubmit" , "return browserID();");
            if (lblMessage.Text == "" && Request["assertion"] != null)
            {
                string assertion = Request["assertion"];
                var audience = Request.Url.Host + ":" + Request.Url.Port.ToString();
                var browID = new BrowserID.Net.BrowserId(audience,  assertion);
                 if (browID.VerifyAssertion())
                {
                    var users = Membership.FindUsersByEmail(browID.Email);
                      if (users.Count > 0) // yes this is a valid user, log them in
                    {
                        FormsAuthentication.SetAuthCookie(browID.Email, true, "/");
                        FormsAuthentication.RedirectFromLoginPage(browID.Email, true, "/");
                     }
                      else
                     {
                          Response.Redirect("~/CreateUser.aspx");
                     }

                 }
                 else
                {
                    lblMessage.Text = "Identification failure";
                }
            }
        }
    }
}

Note that I am using the user's Email address for their username in the Membership tables. There is no need for a password, so I just re-use the email address for that in the CreateUser method:

protected void CreateUserWizard1_ContinueButtonClick(object sender, EventArgs e)
         {
             if (!Roles.RoleExists("user"))
                 Roles.CreateRole("user");
            Roles.AddUserToRole(CreateUserWizard1.Email, "user");
            FormsAuthentication.RedirectFromLoginPage(CreateUserWizard1.Email, true);
        }

Is that super-easy, or what!

The configuration of the web.config should look very familiar:

<connectionStrings>
    <remove name="LocalSqlServer"/>
     <add name="LocalSqlServer" connectionString="server=(local);database=test;Integrated Security=SSPI" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/Login.aspx" defaultUrl="~/Default.aspx" cookieless="UseCookies"  enableCrossAppRedirects="true" path="/"  />
    </authentication>
    <anonymousIdentification enabled="true" cookieTimeout="30" cookieName=".anon" />
    <membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="20" >
      <providers>
        <add connectionStringName="LocalSqlServer" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false"
          
passwordFormat="Hashed" applicationName="/" minRequiredPasswordLength="1" requiresUniqueEmail="true" minRequiredNonalphanumericCharacters="0"
          
name="SqlProvider" type="System.Web.Security.SqlMembershipProvider" />
      </providers>
    </membership>
    <roleManager enabled="true" defaultProvider="SqlProvider">
      <providers>
        <clear />
        <add connectionStringName="LocalSqlServer" applicationName="/" name="SqlProvider"
          
type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
      </providers>
    </roleManager>


In my sample ASP.NET Web Application I've provided  four pages and one user control:

1) Install.aspx - this configures your "TEST" database for Membership, Roles, and Profiles. You need to create the database in SQL Server first.
2) ControlTest.aspx - this is the host page for the user control. It has no code.
3) Login.ascx - The BrowserId UserControl to drop on any page.
4) Default.aspx - the default page which has a LoginName control on it.
5) CreateUser.aspx - A page to create a new user, with a CreateUserWizard control on it.

Everything else is quite "Plain Vanilla". With this ammunition, you are all ready to start using BrowserID with FormsAuthentication, Membership, Roles and Profile.

You can download the working Visual Studio 2010 solution here.

By Peter Bromberg   Popularity  (7437 Views)