22

How can I save something using FormsAuthentication? I don't want to store UserId through URL's.

For example, now I have this code:

//UserController class:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
  if (repository.ValidateUser(model.Login, model.Password))
  {
    FormsAuthentication.SetAuthCookie(model.Login, model.RememberMe);
    if (Url.IsLocalUrl(returnUrl))
    {
      return Redirect(returnUrl);
    }
    else
    {
      return RedirectToAction("Project", "Index");
    }
  }
  else
  {
     ModelState.AddModelError("", "Incorrect name or password.");
  }
}

return View(model);
}

ProjectController class:

public ViewResult Index()
{
    return View(repository.GetUserProjects(
        this.ControllerContext.HttpContext.User.Identity.Name));
}

ProjectRepository:

ProjectsContext context = new ProjectsContext();
UsersContext uCnt = new UsersContext();

public IEnumerable<Project> GetUserProjects(String username)
{
    if (String.IsNullOrEmpty(username))
        throw new ArgumentNullException("username", "Login is empty");
    return this.uCnt.Users
               .FirstOrDefault(u => u.Login == username)
               .Projects
               .ToList();
}

ProjectController and ProjectRepository don't looks like good code... Maybe someone can give advise, how to store UserID without using URL's? Best way to do this is save IDs on autorisation, I think. I don't found any properties in User.Identity to do this...

UPD

I beg a pardon, but I forgot to say that I'm using MVC-3 with Razor view. And that UserId is not a string (User.Identity.Name is a string) it could be GUID or maybe my own object...

4 Answers 4

39
+25

Save the UserID in the UserData property of the FormsAuthentication ticket in the authorization cookie when the user logs on:

string userData = userID.ToString();

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, user.Email,
    DateTime.Now, DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),
    createPersistentCookie, userData);
string hashedTicket = FormsAuthentication.Encrypt(ticket);

HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashedTicket);
HttpContext.Current.Response.Cookies.Add(cookie);

You can read it back in the PostAuthenticateRequest method in Global.asax:

HttpCookie formsCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

if (formsCookie != null)
{
    FormsAuthenticationTicket auth = FormsAuthentication.Decrypt(formsCookie.Value);

    Guid userID = new Guid(auth.UserData);

    var principal = new CustomPrincipal(Roles.Provider.Name, new GenericIdentity(auth.Name), userID);

    Context.User = Thread.CurrentPrincipal = principal;
}

Note that in this case, CustomPrincipal derives from RolePrincipal (although if you're not using Roles, I think you need to derive from GenericPrincipal), and simply adds the UserID property and overloads the constructor.

Now, wherever you need the UserID in your app, you can do this:

if(HttpContext.Current.Request.IsAuthenticated)
    Guid userID = ((CustomPrincipal)HttpContext.Current.User).UserID;
Sign up to request clarification or add additional context in comments.

2 Comments

Thats make cents... I'll try it day after tomorrow.
The first part where you save the UserId in UserData seems incomplete. Can someone explain or wrap that code in the proper class so I can see where that code lives? Thanks.
5

Why not first make all your authorization calls via an interface. This way all of your code which uses authentication does not need to be concerned about how the login is performed, or how the Indentity is stored, etc.

public interface IAuthorization
{
    bool ValidateUser(LoginUser u, string password);
    LoginUser GetCurrentUser();
    void LogIn(LoginUser user);
    void LogOut();
    IIdentity GetCurrentUserIdentity();
}

Implemenation for the IIdentity GetCurrentUserIdentity could be any way you like, but is commonly seen as a call to "HttpContext.Current.User.Identity"

public class Authorization : IAuthorization
{
    /// <summary>
    /// Get the IIdentity for the current logged in user
    /// </summary>
    /// <returns>IIdentity</returns>
    public virtual IIdentity GetCurrentUserIdentity()
    {
        return HttpContext.Current.User.Identity;
    }

    /// <summary>
    /// Log the user in
    /// </summary>
    /// <param name="user">User details</param>
    public void LogIn(LoginUser user)
    {
        InvalidCredentialsOnNullUser(user);
        FormsAuthentication.SetAuthCookie(user.Name, false);
    }

    /// <summary>
    /// Log the user out
    /// </summary>
    public void LogOut()
    {
        FormsAuthentication.SignOut();
    }

    private static void InvalidCredentialsOnNullUser(LoginUser user)
    {
        if (user == null)
        {
            throw new InvalidCredentialException("That user doesn't exist or is not valid.");
        }
    }

    // other methods....

}

The LoginUser class you see is information which is retrieved about a membership user. This is commonly done via a MembershipProvider but of course can be done other ways.

public class LoginUser
{
    public string Name;
    public Guid Key;
    public string EmailAddress;
    public bool IsApproved;
    public bool IsLockedOut;
    public DateTime CreationDate;
    public DateTime? LastLoginDate;
    public DateTime? LastPasswordChangedDate;
}

1 Comment

I have no idea why anyone would still want to use the membership provider, that thing is and has been trash ever since it came out. Role your own auth.
1

I'm not sure I understand the question correctly but if you're referring to a way of retrieving who the current user is without passing it through the URL (e.g. http://localhost/controller/action?username=RAMe0) then you can look at using Thread.CurrentPrincipal.Identity.Name or HttpContext.Current.User

There are subtle differences between the two however. Look here for more details.

3 Comments

Hmm... I don't have Current member in HttpContext... And another thing: where from will comes Current if I'll reload page? But steel I don't have Current in HttpContext :(
Make sure you have a reference to System.Web.dll and that the top of your code files has "using System.Web;"
Nice article about the differences, wasn't aware of that.
1

Using FormsAuthentication you can store the Username in the User.Identity.Name property. Here's a simple example of what you probably are looking for. (Using the same SetAuth you're already using)

public ViewResult Index() {
    return View(repository.GetUserProjects(this.User.Identity.Name));
}

This doesn't require you to pass the username in through a QueryString parameter.

4 Comments

Thats exactly what I'm doing... I want to store UserID and maybe leter som other info, using SetAuthCookie...
The original write up used the interface methods which is why I reproduced the Login method. I rewrote it after I realized you were the FormsService. But I wanted to really focus on the second block which uses this.User.Identity.Name.
You could always store more information in the User.Identity.Name (like a json if your data is short). You could also implement your own IIdentity object. Do you want to basically store all the user information in the User.Identity? It's hard to tell from your question.
I realise this... That was my first question and It's not so clear as it looks like before... I'm not sure about security when you store and check only user login. So I want to save in cookies not only Login but password to. (And maybe some additional info)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.