C# – Getting the current user id (not name) using forms authentication

asp.netasp.net-membershipasp.net-mvccforms-authentication

Question:

I'm playing around with forms authentication and my own custom membership provider.

From what I see, I can get the current FormsIdentity by doing:

System.Web.Security.FormsIdentity veryFunny= (System.Web.Security.FormsIdentity)
    System.Web.HttpContext.Current.User.Identity;

And I can get the current Membership user doing this:

        var UserFromDb = System.Web.Security.Membership.GetUser();

And then I can get the userid by doing this:

var MyUserId = UserFromDb.ProviderUserKey;

What I find funny is that when I call Membership.GetUser(), then it calles this method in the membership provider

public override MembershipUser GetUser(string username, bool userIsOnline)

Which looks up the user information in the database.
So what I figured is that the .NET framework internally does

GetUser(System.Web.HttpContext.Current.User.Identity.Name, whatever);

which gets the user info from the database based on the USERNAME.
I find this disturbing, first because it is bad for performance when I have to lookup an entire user just to get the userid.

Second, it's disturbing that i have to lookup the userid at all, because that's not what I would expect.

Third, it's disturbing, because the username can be changed in the course of the program, and it's nonsense to have the user having to logout and login to do that.

Now, to me this design sounds like nonsense.
But then again, microsoft also uses application name, group name, and username as primary key, which also really does not make a lot of sense.

So here my question:
Isn't there a way to get the user ID without a DB lookup ?

Or is the entire Membership provider idea just so broken by design ?

If it is so broken:
I saw that FormsIdentity has a string property called userdata.
If so, how can I use ASP.NET to save the userid in there ?

Best Answer

Yes, one can with a workaround from here.
One can set the UserId in the Auth cookie-Ticket UserData.

public class UserData
{
    public string FirstName { get; set; }
    public UserData()
    {
        FirstName = "Unknown";
    }
} // End Class UserData



public void SignIn(string userName, bool createPersistentCookie)
{
    if (String.IsNullOrEmpty(userName)) 
        throw new ArgumentException("Der Wert darf nicht NULL oder leer sein.", "userName");

    UserData userData = new UserData();
    SetAuthCookie(userName, createPersistentCookie, userData);
    //FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}


public void SetAuthCookie(string userName, bool createPersistentCookie, UserData userData)
{
    HttpCookie cookie = FormsAuthentication.GetAuthCookie(userName, createPersistentCookie);
    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
    FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(
         ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration
        ,ticket.IsPersistent, userData.ToJSON(), ticket.CookiePath
    );

    string encTicket = FormsAuthentication.Encrypt(newTicket);
    cookie.Value = encTicket;
    System.Web.HttpContext.Current.Response.Cookies.Add(cookie);
} // End Sub SetAuthCookie

Another possibility is to just use the UserId.ToString() as "name".