Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Update FormsAuthenticationTicket UserData at Runtime

I am writing an MVC 4 web application with custom authentication and authorisation. When a user logs into the site, I create a a FormsAuthenticationTicket and store it in a cookie

public void SignIn(string userName, bool createPersistentCookie, string UserData)
{
    if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");

    // Create and tuck away the cookie
    FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, userName, DateTime.Now, DateTime.Now.AddDays(15), createPersistentCookie, UserData);
    // Encrypt the ticket.
    string encTicket = FormsAuthentication.Encrypt(authTicket);

    //// Create the cookie.
    HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
    HttpContext.Current.Response.Cookies.Add(faCookie);
}

The UserData string will be a pipe delimited string and it will always contain at least two items, UserID | UserRole. A user can be assigned to one or more roles, therefore, the UserData could look like this UserID | UserRole | UserRole | UserRole

I then have my own custom generic principal in Global.asax

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{

        // Get the authentication cookie
        string cookieName = FormsAuthentication.FormsCookieName;
        HttpCookie authCookie = Context.Request.Cookies[cookieName];

        // If the cookie can't be found, don't issue the ticket
        if (authCookie == null) return;

        // Get the authentication ticket and rebuild the principal
        // & identity
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

        string[] UserData = authTicket.UserData.Split(new Char[] { '|' });

        GenericIdentity userIdentity = new GenericIdentity(authTicket.Name);
        GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity, UserData);
        Context.User = userPrincipal;
}

This all works fine, however, within my application, if a user has multiple roles, when they log in, I need to list their roles, and then let them select only one role to go and perform functionality based on the selected role.

I was thinking, to do this, maybe I could pass the role the user selects to a method, get their FormsAuthenticationTicket and update the UserData to reflect the role they have choosen. For example, a UserData string is created with 1|Manager|Applicant, then I need to list both roles and ask the user which role they want to perform functionality under, they select Manager and I then update their UserData within their FormsAuthenticationTicket to 1|Manager.

Is this even possible, or maybe there is a better way of doing this?

Any help would be greatly appreciated.

Thanks everyone.

like image 424
tcode Avatar asked Feb 13 '13 14:02

tcode


2 Answers

You could always change FormsAuthenticationTicket.

HttpCookie cookie = FormsAuthentication.GetAuthCookie(Username, true);
var ticket = FormsAuthentication.Decrypt(cookie.Value);

var newticket = new FormsAuthenticationTicket(ticket.Version,
                                              ticket.Name,
                                              ticket.IssueDate,
                                              ticket.Expiration,
                                              true, 
                                              "new user data",
                                              ticket.CookiePath);

cookie.Value = FormsAuthentication.Encrypt(newticket);
cookie.Expires = newticket.Expiration.AddHours(24);
HttpContext.Current.Response.Cookies.Set(cookie);
like image 66
Dzendo Avatar answered Oct 06 '22 23:10

Dzendo


As I said in my comment, I feel this is very poor usability. However, if you're determined to do this, then Dzenan's approach would work (you essentially just strip out all the other roles after the user has selected which role he wants).

Another approach would be to add an additional field to your userdata, which is SelectedRole. Then create a custom IPrincipal that includes this field.

Another approach would be to store it as your own cookie, although I would make sure that you validate that you aren't setting a role that the user is not authorized for (ie if you set SelectedRole=Admin make sure the user has an Admin role before you use it).

like image 42
Erik Funkenbusch Avatar answered Oct 07 '22 00:10

Erik Funkenbusch