views:

132

answers:

2

I have had some problems with authentication in ASP.NET. I'm not used most of the built in authentication in .NET.

I gotten some complaints from users using Internet Explorer (any version - may affect other browsers as well) that the login process proceeds but when redirected they aren't authenticated and are bounced back to loginpage (pages that require authentication check if logged in and if not redirect back to loginpage). Can this be a cookie problem?

Do I need to check if cookies are enabled by the user?

What's the best way to build authentication if you have a custom member table and don't want to use ASP.NET login controls?

Here my current code:

using System;
using System.Linq;
using MyCompany;
using System.Web;
using System.Web.Security;
using MyCompany.DAL;
using MyCompany.Globalization;
using MyCompany.DAL.Logs;
using MyCompany.Logging;

namespace MyCompany
{

    public class Auth
    {

        public class AuthException : Exception
        {
            public int StatusCode = 0;
            public AuthException(string message, int statusCode) : base(message) { StatusCode = statusCode;  }
        }

        public class EmptyEmailException : AuthException
        {
            public EmptyEmailException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMPTY_EMAIL, 6) { }
        }

        public class EmptyPasswordException : AuthException
        {
            public EmptyPasswordException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMPTY_PASSWORD, 7) { }
        }

        public class WrongEmailException : AuthException
        {
            public WrongEmailException() : base(Language.RES_ERROR_LOGIN_CLIENT_WRONG_EMAIL, 2) { }
        }

        public class WrongPasswordException : AuthException
        {
            public WrongPasswordException() : base(Language.RES_ERROR_LOGIN_CLIENT_WRONG_PASSWORD, 3) { }
        }

        public class InactiveAccountException : AuthException
        {
            public InactiveAccountException() : base(Language.RES_ERROR_LOGIN_CLIENT_INACTIVE_ACCOUNT, 5) { }
        }

        public class EmailNotValidatedException : AuthException
        {
            public EmailNotValidatedException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMAIL_NOT_VALIDATED, 4) { }
        }

        private readonly string CLIENT_KEY = "9A751E0D-816F-4A92-9185-559D38661F77";

        private readonly string CLIENT_USER_KEY = "0CE2F700-1375-4B0F-8400-06A01CED2658";

        public Client Client
        {
            get
            {
                if(!IsAuthenticated) return null;
                if(HttpContext.Current.Items[CLIENT_KEY]==null)
                {
                    HttpContext.Current.Items[CLIENT_KEY] = ClientMethods.Get<Client>((Guid)ClientId); 
                }
                return (Client)HttpContext.Current.Items[CLIENT_KEY];
            }
        }

        public ClientUser ClientUser
        {
            get
            {
                if (!IsAuthenticated) return null;
                if (HttpContext.Current.Items[CLIENT_USER_KEY] == null)
                {
                    HttpContext.Current.Items[CLIENT_USER_KEY] = ClientUserMethods.GetByClientId((Guid)ClientId);
                }
                return (ClientUser)HttpContext.Current.Items[CLIENT_USER_KEY];
            }
        }

        public Boolean IsAuthenticated { get; set; }

        public Guid? ClientId { 
            get 
            {
                if (!IsAuthenticated) return null;
                return (Guid)HttpContext.Current.Session["ClientId"];
            } 
        }

        public Guid? ClientUserId { 
            get {
                if (!IsAuthenticated) return null;
                return ClientUser.Id;
            } 
        }

        public int ClientTypeId { 
            get {
                if (!IsAuthenticated) return 0;
                return Client.ClientTypeId;
            } 
        }

        public Auth()
        {
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                IsAuthenticated = true;
            }
        }

        public void RequireClientOfType(params int[] types)
        {
            if (!(IsAuthenticated && types.Contains(ClientTypeId)))
            {
                HttpContext.Current.Response.Redirect((new UrlFactory(false)).GetHomeUrl(), true);
            }
        }

        public void Logout()
        {
            Logout(true);
        }

        public void Logout(Boolean redirect)
        {
            FormsAuthentication.SignOut();
            IsAuthenticated = false;
            HttpContext.Current.Session["ClientId"] = null;
            HttpContext.Current.Items[CLIENT_KEY] = null;
            HttpContext.Current.Items[CLIENT_USER_KEY] = null;
            if(redirect) HttpContext.Current.Response.Redirect((new UrlFactory(false)).GetHomeUrl(), true);
        }

        public void Login(string email, string password, bool autoLogin)
        {
            Logout(false);

            email = email.Trim().ToLower();
            password = password.Trim();

            int status = 1;

            LoginAttemptLog log = new LoginAttemptLog { AutoLogin = autoLogin, Email = email, Password = password };

            try
            {
                if (string.IsNullOrEmpty(email)) throw new EmptyEmailException();

                if (string.IsNullOrEmpty(password)) throw new EmptyPasswordException();

                ClientUser clientUser = ClientUserMethods.GetByEmailExcludingProspects(email);

                if (clientUser == null) throw new WrongEmailException();

                if (!clientUser.Password.Equals(password)) throw new WrongPasswordException();

                Client client = clientUser.Client;

                if (!(bool)client.PreRegCheck) throw new EmailNotValidatedException();

                if (!(bool)client.Active || client.DeleteFlag.Equals("y")) throw new InactiveAccountException();

                FormsAuthentication.SetAuthCookie(client.Id.ToString(), true);
                HttpContext.Current.Session["ClientId"] = client.Id;

                log.KeyId = client.Id;
                log.KeyEntityId = ClientMethods.GetEntityId(client.ClientTypeId);
            }
            catch (AuthException ax)
            {
                status = ax.StatusCode;
                log.Success = status == 1;
                log.Status = status;
            }
            finally
            {
                LogRecorder.Record(log);
            }

        }

    }

}
+8  A: 

A classic case of over-engineered Authentication mechanism and on top of it the design is bad.

  • Exceptions should be out of Auth class but reside in same namespace. Can you imagine how .Net framework would look if Microsoft had created exceptions like this. Always keep it simple and stupid (KISS). It seems you need modular code. Try to be simple yet modular.

  • Your authentication Client-Keys are static magic-values and you're shipping them with your assemblies. Use SecureString instead of readonly string. Anybody can get hold of it using Reflector. How do you sustain change ad security?

  • Your code directly refers Current HttpContext object when in fact you could have passed the reference of current context object in client-code that will use this.

  • RequireClientOfType is int[] - why in the world you want to do this ? I believe it could have been an enum or an immutable struct if at all ever needed.

  • You are already using FormsAuthentication in your Login() and Logout() which is sufficient to replace your entire Auth. Why do you want to re-invent the wheel if ultimately you are going to use FormsAuthnetication to take care of Auth.

  • And yes if you cannot revise this design please use FxCop/StyleCop at least to avoid spaghetti-code.

  • Also you could make class Auth as static and expose functionalities like FormsAuthentication does. And also rename it from Auth to Authentication.

This is a prime candidate for http://thedailywtf.com/

this. __curious_geek
Thanks for good answer! but... Keep it simple and stupid? :)
Niels Bosma
+1 for secure string reference; http://blogs.techrepublic.com.com/programming-and-development/?p=412
KMan
+3  A: 

Try using built-in asp.net Forms Authentication (Membership).

You can learn from these videos:

Link1 and Link2

If you want to customize it watch this video:

Link

Krunal