title: Users able to see others' details. FirstOrDefault returning the wrong record? Or caching issue? Help! :)
body: <p>Users of my site have experienced some strange behaviour yesterday (first time I've seen this issue), and unfortunately I don't have much in the way of error logs to try to figure out what's going on.  The site had a higher-than-normal number of people online at once, albeit not a large number in the grand scheme of things (maybe 50 to 100 users all trying to perform similar functions).  I can't recreate the issue in my development environment, haven't seen it before, and don't really know why it is happening.</p>
  
  <p>The crux of the problem is that users can register or log on successfully, but a small number of them could see other users' data.</p>
  
  <p>The site is ASP.NET MVC 3.</p>
  
  <p>Users are logging on and I set an authentication cookie - here's the LogOn action:</p>
  
  <pre><code>    [HttpPost]
      public ActionResult LogOn(AccountLogOnViewModel model, string returnUrl)
      {
          if (ModelState.IsValid)
          {
              if (!Membership.ValidateUser(model.UserName, model.Password))
              {
                  ModelState.AddModelError("login-message", "Incorrect username or password");
              }
          }
  
          if (ModelState.IsValid)
          {
              FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
              Session.Remove("MenuItems");
              return Redirect(returnUrl ?? Url.Action("Index", "Home"));
          }
          else
          {
              model.ReturnUrl = returnUrl;
              return View(model);
          }
      }
  </code></pre>
  
  <p>AccountLogOnViewModel is a simple object with two string properties, UserName and Password.</p>
  
  <p>From what I can gather, this is fine - if you log in as NickW then doing something like User.Identity.Name correctly gives you "NickW" (when users were seeing other users' data, they reported that that "Welcome, NickW" text on screen was showing them the correct value - this is written out using User.Identity.Name)</p>
  
  <p>The site also uses a custom membership provider.  It overrides the ValidateLogin method, and the GetUser method.  ValidateLogin appears to be working just fine so I'm not concerned about it.</p>
  
  <p>The overridden GetUser method is as follows:</p>
  
  <pre><code>    public override MembershipUser GetUser(string username, bool userIsOnline)
      {
          User user = _userRepository.Users.FirstOrDefault(u =&gt; u.UserName == username);
          MembershipUser membershipUser = null;
  
          if (user == null)
              return membershipUser;
  
          membershipUser = new MembershipUser(this.Name,
              user.UserName,
              user.Id,
              user.Email,
              null,
              user.Comments,
              user.IsActivated,
              user.IsLockedOut,
              user.CreatedDate,
              user.LastLoginDate,
              user.LastLoginDate,
              user.LastModifiedDate,
              Convert.ToDateTime(user.LastLockedOutDate));
  
          return membershipUser;
      }
  </code></pre>
  
  <p>So I'm attempting to retrieve a User object from my database, and using that to create a new MembershipUser object.  My database User table has additional columns on top of those required by the membership provider - e.g. name, address, phone number etc.</p>
  
  <p>At various points in the rest of the website (for example if you go to the Profile page), I retrieve a user object from the database and use it to populate the screen.  The line I use to retrieve the User object is:</p>
  
  <pre><code>User user = userRepository.Users.FirstOrDefault(u =&gt; u.UserName == Membership.GetUser().UserName);
  </code></pre>
  
  <p>Here is a cut down version of the userRepository (i.e. just removing unrelated code).</p>
  
  <pre><code>public class SqlUserRepository : IUserRepository
  {
      private Table&lt;User&gt; usersTable;
      private string _connectionString;
  
      public SqlUserRepository(string connectionString)
      {
          _connectionString = connectionString;
          usersTable = (new DataContext(connectionString)).GetTable&lt;User&gt;();
      }
  
  
      public IQueryable&lt;User&gt; Users
      {
          get { return usersTable; }
      }
  
      public void CreateUser(AccountRegisterViewModel user)
      {
          User newUser = new User();
  
          newUser.UserName = user.UserName;
          newUser.Salutation = user.Salutation;
          newUser.PhoneNumber = user.PhoneNumber;
          newUser.SecondaryPhoneNumber = user.SecondaryPhoneNumber;
          newUser.FirstName = user.FirstName;
          newUser.LastName = user.LastName;
          newUser.PasswordSalt = CreateSalt();
          newUser.Password = CreatePasswordHash(user.Password, newUser.PasswordSalt);
          newUser.Email = user.Email;
          newUser.CreatedDate = DateTime.UtcNow;
          newUser.Comments = "Created from web registration";
          newUser.LastModifiedDate = DateTime.UtcNow;
          newUser.LastLoginDate = DateTime.UtcNow;
          newUser.IsActivated = true;
          newUser.IsLockedOut = false;
          newUser.MayContact = user.MayContact;
  
          usersTable.InsertOnSubmit(newUser);
          usersTable.Context.SubmitChanges();
      }
  }
  </code></pre>
  
  <p>So it appears to me as if the auth cookie I'm setting is fine, but either:
  When I first go in to the membership provider's GetUser() method, it retrieves the wrong record from the database and therefore sets up a MembershipUser object with the wrong username; subsequently when I look in the database for "this" user I'm actually looking for the wrong username.</p>
  
  <p>Or: Intermittently when I do userRepository.FirstOrDefault(x => x.UserName == Membership.GetUser().Name) it retrieves the wrong record.</p>
  
  <p>Or: something else is going wrong that I haven't thought of.</p>
  
  <p>As I say, this seems to be a problem when the site was under load, so I'm wondering if it's some sort of caching issue somewhere?  But I really don't know.</p>
  
  <p>One thought I had was to change the way I retrieve the user in case the problem lies with the membership provider, and use this instead:</p>
  
  <pre><code>userRepository.FirstOrDefault(x =&gt; x.UserName == User.Identity.Name)
  // or HttpContext.Current.User.Identity.Name if not within a controller
  </code></pre>
  
  <p>But really I'm not even sure what's going on so have no idea whether this will resolve the issue.  Could it be a caching problem somewhere?  It appears (but I can't be 100% certain) that when user A could see user B's details, it was always the case that user B was also active in the system (or had been within the previous 20 minutes).</p>
  
  <p>I know it's a long shot, but does anyone have any idea how this could happen?  Obviously it's a major concern and needs to be fixed urgently, but without knowing why it's happening I can't fix it!</p>
  
  <p>Thanks in advance for any help,
  Nick</p>