Websites often sends emails to registered users with inivitations to make certain actions. Sometimes we need to send user an URL to the registered area of site. The standart approach is to send a link to the page, users follow it, ASP.NET regirects him to login page where he must enter his credentions to continue. In some applications users receives an URL wich automatically logs user in (for example in LinkedIn). This article is about an ASP.NET module wich automatically logs users in case of special URL signature found in URL.

The approach

How to sign the URL and verify, that the signature is given to the certain user? My approach is to set 2 url params: with user name and with the signature based on machine validation key of web application. If we add the signature to the url string with the user name we can be sure this user has rights to see this page. Of couse this is not 100% secure method but, because the URL can be stolled or sniffered but in many cases such a security is acceptable. The machine key verification key is rather strong and secure for the purpose. The solution in the form of HttpModule is reusable. All we have to do is to handle AuthenticateRequest event and authenticate user in it.

The code

Full source of UrlAutoLogInModule can be downloaded here: UrlAutoLogInModule.zip (2.19 kb)

In the code a HttpModule created

    public class UrlAutoLogInModule : IHttpModule

which simple handles AuthenticateRequest event

        public void Init(HttpApplication context)

        {

            // Handle the authenticate request event

            context.AuthenticateRequest += new EventHandler(AuthenticateRequest);

        }

The module search for the get params with user name and signature

        protected void AuthenticateRequest(object sender, EventArgs e)

        {

            HttpApplication application = (HttpApplication) sender;

            string authToken = application.Request.QueryString[TokenParam];

            string userName = application.Request.QueryString[UserNameParam];

 

            if (string.IsNullOrEmpty(authToken) || string.IsNullOrEmpty(userName))

                return;

Next step is check if such a user is not locked or disabled to prevent locked users log in into website. The user retrievment will also updates the last login time in the database. At this point an assumption that the Membership is used to implement website security.

                MembershipUser muser = Membership.GetUser(userName, true);

                if (!muser.IsApproved || muser.IsLockedOut)

                {

                    return;

                }

The futher logic is very simple. We have to compute hash for the request URL and chek if it equals to the signature in the get params. If verification succeded we add an authentication cookie for the user and initialize System.Threading.Thread.CurrentPrincipal and System.Web.HttpContext.Current.User. You can add some custom logic here if you need to use other than GenericPrincipal implementation of IPrincipal interface. To prevent hackers from finding verification key it will be nice to lock user due to membership settings after several failed login attempts. To do this it is possible to call Membership.VerifyPassword with wrong password. This will update Membership tables and log failed login attempt.

                string currentHash = ComputeHash(GetFullRawUrl(application.Request));

                bool approve = currentHash.Equals(authToken);

                if (approve)

                {

                    // Add auth cookie

                    FormsAuthentication.SetAuthCookie(userName, false);

                    IPrincipal user = new GenericPrincipal(

                        new GenericIdentity(userName), null

                        );

                    System.Threading.Thread.CurrentPrincipal = user;

                    HttpContext.Current.User = user;

                }

                else

                {

                    Membership.ValidateUser(userName, "invalid password");

                }

There is several odds in ComputeHash method wich I want to describe. First it is removes the signature param from the url string because it is impossible to sign URL with signature. Second it uses Request.RawUrl instead of other request properties because I am using UrlRewriter.NET and in that property ASP.NET stores not rewrited URL from user's browser. And finally it gets verification key from machineKey config section and comutes hash of the URL string. The signature algo is same as in MachineKeySection.HasData method wich unfotunetly is declared as internal (I'm wondering why).

How it works

The module needs a registration in the web.config file httpModules section

      <httpModules>

        <add name="UrlAutoLogInModule" type="Yesnobox.Modules.UrlAutoLogInModule, YourAssembly" /> 

If you want to send user a signed URL, you have to call static method wich adds signature and user name to the URL.

emailArgs.TestUrl = Yesnobox.Modules.UrlAutoLogInModule.SignUrl(emailArgs.TestUrl, ud.UserName); 

 

When user follows this url and his signature is valid he automatically logs in and see page without redirecting to the login page. If the signature is wrong and the page can be seen only by logged in user the user will be redirected to the login page by internal ASP.NET logic.

If you care about security much it is possible to add an expiretion date for the signature by simply add expiration date to one of URL param before adding the signature hash.

I created a simple example wich uses the module.  AutoLoginExample.zip (11.12 kb)

Here there is auto login link on the page. If the user followes it he gets authorised. Note that there is no logic to login user in the page onbehind or at any place of the site. There is only a signed url on the page that is doing automatical login.

Submit this story to DotNetKicks

Posted in: ASP.NET  Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading