1

I do not understand what's going on with FormsAuthentication. When I first turn on my PC and start working on my project, everytime I try to login, I get an error telling me that the queried username (nickname in my case) is not found the sequence. I check its value while debugging, it turns out it's a null string (""). Then if I stop debugging then start it again, I get logged in normally and it picks up the right username.

Moreover, if I try to login with another user from another browser, the same thing happens but eventually it picks up the same user that was logged in using the same browser! Does FormsAuthentication use the same ticket in all browsers, for all users? Is there anything I can do about it? Or could it be that I'm doing something wrong here...?

Here's my code:

    [HttpPost]
    public ActionResult Login(LoginViewModel dto)
    {  
        bool flag = false;
        if (ModelState.IsValid)
        {
            if (_userService.AuthenticateUser(dto.Email, dto.Password, false))
            {
                var user = _userService.GetUserByEmail(dto.Email);

                flag = true;

                FormsAuthentication.SetAuthCookie(user.Nickname, dto.RememberMe);
            }
        }
        if (flag)
        {
            return RedirectToAction("Index", "Home");
        }
        else
        {
            ViewData.Add("InvalidLogin", "The login info you provided were incorrect.");
            return PartialView(dto);
        }
    }

    public User GetUserFromSession(bool withAllInfo)
    {
        string nickname = _httpContext.User.Identity.Name;
        IsNotNull(nickname, "session user nickname");
        var user = _userService.GetUserByNickname(nickname, withAllInfo);
        return user;
    }

    public User GetUserFromSession(string nickname, bool withAllInfo)
    {
        if (string.IsNullOrEmpty(nickname))
            return GetUserFromSession(withAllInfo);

        var user = _userService.GetUserByNickname(nickname, withAllInfo);
        return user;
    }

The method above is in a helper class (which takes an instance of HttpContext - using StructureMap). It keeps returning the user with nickname J.Smith even if I logged in with another user. And the funny thing is that it then displays the correctly logged in user using the Summary ActionMethod (see below).

    [Authorize]
    public ActionResult Summary()
    {
        var nickname = this.HttpContext.User.Identity.Name;
        var user = _helper.GetUserFromSession(nickname, true);
        var viewModel = Mapper.Map<User, UserInfoSummaryViewModel>(user);
        return PartialView(viewModel);
    }

This method displays a summary of all the user's info including their bids, listings, new messages... etc. This actually works correctly (in most cases). But the problem is with the GetUserFromSession() method which is messing everything up.

    public ActionResult SignOut()
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Index", "Home");
    }

Is that all I need to do to sign the user out and delete their cookie/session or whatever FormsAuthentication does to manage sessions?

Note: I am not using the built-in Membership API and I do not want to use it.

So, is there anything I can do to fix this mess?

UPDATE:

StructureMap configuration:

    private static IContainer ConfigureStructureMap()
    {

        ObjectFactory.Configure(x =>
                            {
                                x.For<IDatabaseFactory>().Use<EfDatabaseFactory>();
                                x.For<IUnitOfWork>().Use<UnitOfWork>();
                                x.For(typeof (IRepository<>)).Use(typeof (BaseRepository<>));
                                x.For<IGenericMethodsRepository>().Use<GenericMethodsRepository>();
                                x.For<IUserService>().Use<UsersManager>();
                                x.For<IBiddingService>().Use<BiddingService>();
                                x.For<ISearchService>().Use<SearchService>();
                                x.For<IFaqService>().Use<FaqService>();
                                x.For<IItemsService>().Use<ItemsService>();
                                x.For<IPrivateMessagingService>().Use<PrivateMessagingService>();
                                x.For<IStaticQueriesService>().Use<StaticQueriesService>();
                                x.For<ICommentingService>().Use<CommentingService>();
                                x.For<ICategoryService>().Use<CategoryService>();
                                x.For<IHelper>().Use<Helper>();
                                x.For<HttpContext>().Use(HttpContext.Current);

            x.For(typeof(Validator<>)).Use(typeof(NullValidator<>));

            x.For<Validator<Rating>>().Use<RatingValidator>();
            x.For<Validator<TopLevelCategory>>().Use<TopLevelCategoryValidator>();
        });

        Func<Type, IValidator> validatorFactory = type =>
        {
            var valType = typeof(Validator<>).MakeGenericType(type);
            return (IValidator)ObjectFactory.GetInstance(valType);
        };

        ObjectFactory.Configure(x => x.For<IValidationProvider>().Use(() => new ValidationProvider(validatorFactory)));
        return ObjectFactory.Container;
    }
Kassem
  • 8,116
  • 17
  • 75
  • 116

2 Answers2

0

It sounds like the scope fo the _httpContext instance being maintained/created by StrucutureMap is incorrect. Here's some more information on scope--I'd be happy to look into it further if you want to post your StrucutureMap registration code.

ataddeini
  • 4,931
  • 26
  • 34
  • 1
    You are absolutely right. When I moved the code that gets the current authenticated user to the controller itself, the problem was solved! Check out the update, I've added the StructureMap code... Thanks :) – Kassem Apr 25 '11 at 20:03
  • @Kassem: OK, cool. So your registration would probably looks something like `x.For().CacheBy(InstanceScope.HttpContext).Use(HttpContext.Current);` or something similar. Try that out and let me know if it works. – ataddeini Apr 25 '11 at 23:44
  • Unfortunately, that did not work. And now I'm facing a lot of issues because of that (I guess). Even my Session variables are not being defined between ActionMethods... – Kassem Apr 30 '11 at 20:54
  • I started a new question about the issue: http://stackoverflow.com/questions/5844635/session-variables-lost-between-controllers-action-methods – Kassem May 01 '11 at 07:15
  • @Kassem: Shoot, that sucks. I'll take a look a the new question. – ataddeini May 01 '11 at 14:24
0

You could write a HttpContext provider class (interface) that just returns the current HttpContext.Current instance.

using System.Web;

interface IHttpContextProvider
{
    HttpContextBase HttpContext { get; }
}

public class HttpContextProvider : IHttpContextProvider
{
    HttpContextBase HttpContext
    {
        get
        {
            return new HttpContextWrapper(System.Web.HttpContext.Current);
        }
    }
}
Jeremy Bell
  • 2,104
  • 1
  • 14
  • 9