Why redirect? Why not use a Modal Login?

Since the conception of secure pages on the internet, there has been the <pause><chimes> "Login Page" </chimes>. When a user authenticates on this "Login Page" they usually get some sort of a Session set for them, and they're forwarded to a new page. When the authentication times out, or the user logs out, they get redirected to the "Login Page" (yea yea some of us might have a logout page).

 Now with AJAX running rampant on the web, every callback that's done needs to be authenticated to make sure the user is who they way they are. However, as a user, I've loged in. If I want to... go for lunch and then come back to finish entering my form, I don't want to be put to the main login page and then have to re-endter it.

It is bloody annoying at times. Let's say you're... writing a blog and you take more time than alloted to write it. Now, when you click the "Publish" button, you'll be redirected to a login screen.  Now fortunately some newer applications remember what you were trying to do and then start that execution again. However, some just take you back to the initial page... you lose all your work. 

What I've done with the new application I've developed was implement a ModalPopup Login control.  This control sits on my MasterPage, and in the PageLoad it checks to see if the user is logged in.

 

 

The first load of the application takes you to the "Login Page". This is standard as every application needs a starting point (for authentication purposes).  Once the user has fully signed in I set a session showing that the user has loged in the "normal" way.  From then on, every AJAX call, every new postback or page load, my Login Control handles all of the checking.

If the user's session has expired, I simply show the Modal Login control and ask them to re-enter their information. Once it's entered, the user can continue.

 Now there are a few caveats that need to be known using this method.

  1. Some users will try to disable all Styles / JavaScript which can "bypass" this method. Though you can do this authentication on the load of yoru control (Which happens on every callback, or you can use a JavaScript AJAX callback  to check) You still need to make sure all commands you do are done only when the user is authenticated
  2. Someone initiating a postback command like "Show Users" will invoke the ShowUsers page, which will cause this ModalLogin to fire, however the page will load. You can use a solid colour on the background of your modad login, but this brings us back to point #1.

I see only two ways to really secure this. One is to stop all postback events from fireing except the normal page lifecycle. Unfortunately I have not found a way to tell ASP.NET to stop the "OnClick" processing of a button and just continue with the normal page lifecycle.

I have an ongoing ASP.NET Forums post here regarding this. 

The other way is to do this all client side. Create an Authenticate web service that is called from the client on each postback. If the user is not authenticated, show the modal login. When Submited, call another webservice to validate the user. Once validated, hide the form and continue with the postback event.

I will try the 2nd version of this late as that might prove to be a better model.

Though this isn't complete and it's a work in progress, I do want to share it as even as I've been writing this I've lost all I've written twice :) Going from weblogs.asp.net to forums.asp.net changes the cookies and you have to log in seperately for each system. It's  a shame, but I get typing practice.

Here's what I have so far. (note: I cannot share all code as I've done this on my work lappy and I have contracts to follow. The general Gist is here).

login.ascx

 

 

    5 <asp:Panel ID="pnlLogin" runat="server" DefaultButton="btnLogin" CssClass="login" Style="display: none;">

    6     <div class="drag">

    7         <h3>

    8             This_Application Login</h3>

    9     </div>

   10     <div class="tcenter error">

   11         <asp:Label ID="lblMessage" runat="server" />

   12     </div>

   13     <table>

   14         <tr>

   15             <td class="column1">

   16                 Agency ID:</td>

   17             <td class="column2">

   18                 <asp:TextBox ID="txtAgencyID" runat="server" SkinID="DataEntryTextBox" ValidationGroup="Login" />

   19                 <asp:RequiredFieldValidator ID="rfvAgencyID" runat="server" ControlToValidate="txtAgencyID" Text="*" ErrorMessage="AgencyID is Required." ValidationGroup="Login" />

   20                 <ajaxToolkit:ValidatorCalloutExtender ID="vAgencyID" runat="server" TargetControlID="rfvAgencyID" HighlightCssClass="validatorCalloutHighlight" />

   21             </td>

   22         </tr>

   23         <tr>

   24             <td>

   25                 Username:</td>

   26             <td>

   27                 <asp:TextBox ID="txtUserName" runat="server" SkinID="DataEntryTextBox" />

   28                 <asp:RequiredFieldValidator ID="rfvUserName" runat="server" ControlToValidate="txtUserName" Text="*" ErrorMessage="A Username is Required." ValidationGroup="Login" />

   29                 <ajaxToolkit:ValidatorCalloutExtender ID="vUserName" runat="server" TargetControlID="rfvUserName" HighlightCssClass="validatorCalloutHighlight" />

   30             </td>

   31         </tr>

   32         <tr>

   33             <td>

   34                 Password:</td>

   35             <td>

   36                 <asp:TextBox ID="txtPassword" runat="server" SkinID="DataEntryTextBox" TextMode="password" />

   37                 <asp:RequiredFieldValidator ID="rfvPassword" runat="server" ControlToValidate="txtPassword" Text="*" ErrorMessage="A Password is Required." ValidationGroup="Login" />

   38                 <ajaxToolkit:ValidatorCalloutExtender ID="vPassword" runat="server" TargetControlID="rfvPassword" HighlightCssClass="validatorCalloutHighlight" />

   39             </td>

   40         </tr>

   41         <tr>

   42             <td colspan="2">

   43                 <asp:Button ID="btnLogin" runat="server" Text="Login" OnClick="btnLogin_Click" ValidationGroup="Login" />

   44             </td>

   45         </tr>

   46     </table>

   47 </asp:Panel>


 Login.ascx.cs

 

   16 public partial class Login : System.Web.UI.UserControl

   17     {

   18 

   19         protected void Page_Load(object sender, EventArgs e)

   20         {

   21             //check Auth

   22             if (((BasePage)Page).AuthUser == null)

   23             {

   24                 lblMessage.Text = "Application was unable to authorize you. Please re-enter your credentials.";

   25                 mpeLogin.Show();

   26 

   27             }

   28         }

   29 

   30         protected void btnLogin_Click(object sender, EventArgs e)

   31         {

   32             AuthUser auth = new AuthUser(System.Configuration.ConfigurationManager.ConnectionStrings["ConfigConnectionString"].ConnectionString);

   33             User user = new User();

   34             StatusInfo statusInfo = new StatusInfo();

   35 

   36             auth.User.UserName = txtUserName.Text;

   37             auth.User.Password = txtPassword.Text;

   38             auth.Agency.Login = txtAgencyID.Text;

   39             statusInfo = user.Login(auth);

   40 

   41             if (statusInfo.Successful)

   42             {

   43                 ((BasePage)Page).AuthUser = auth;

   44                 mpeLogin.Hide();

   45             }

   46         }

   47 

   48     }


MasterPage.Master

 

   74         <asp:UpdatePanel ID="upLogin" runat="server" UpdateMode="conditional">

   75             <ContentTemplate>

   76                 <sv:Login id="svLogin" runat="server" />

   77             </ContentTemplate>

   78         </asp:UpdatePanel>   

 

No Comments