4

I'm trying to do what should be a simple thing in MVC3.

I've got an application that uses forms authentication to authenticate users with a 3rd party SSO. The SSO, on successful login, posts back to a specific controller action on my application. I then call FormsAuthentication.SetAuthCookie(user,false);.

I'm trying to implement some level of authorization. Simply, a user can exist in a number of different roles, e.g. Admin and Developer. Some controller actions should only be available to certain roles. Details of which roles a user belongs to is obtained by making a call to another external API, which returns a simple JSON response indicating.

In theory, this should be as simple as doing something like this after I set the FormsAuthentication cookie:

string[] rolelist = GetRoleListForUserFromAPI(User.Identity.Name); HttpContext.User = new GenericPrincipal(User.Identity, rolelist); 

However, I can't call this directly after calling SetAuthCookie, because HttpContext.User isn't anything meaningful at this point.

I could try setting this on every request, but ever request to my app would mean a roundtrip API call.

The most promising approach I've seen so far is to create a custom Authorization attribute and override OnAuthorization to do something like this:

public override void OnAuthorization(AuthorizationContext filterContext) { if (<some way of checking if roles have already been set for this user, or role cache has timed out>) { string[] rolelist = GetRoleListForUserFromAPI(filterContext.HttpContext.User.Identity.Name); filterContext.HttpContext.User = new GenericPrincipal(filterContext.HttpContext.User.Identity,rolelist); } } 

I could then use [MyCustomAuthorization(Roles="Admin")] in front of controller actions to make the magic happen.

However, I've no idea how to detect whether or not the current HttpContext.User object has had its roles set, or whether it was set over a certain time ago and another API trip is needed.

What's the best approach for this?

0

4 Answers 4

2

Another way would be to store the roles in the UserData property of the FormsAuthentcationTicket. This could be done with comma delimited string.

http://msdn.microsoft.com/en-us/library/system.web.security.formsauthenticationticket.formsauthenticationticket

Then on AuthenticateRequest method, you could pull the ticket back, grab the roles data and assign it to the current user using a generic principal.

Sign up to request clarification or add additional context in comments.

1 Comment

This is a good idea - I could feasibly store a caching timout value, in case I want to refresh the role list more than once during the user session (say, every 5 minutes).
2

You should override PostAuthenticateRequest

protected void Application_OnPostAuthenticateRequest(object sender, EventArgs e) { if (HttpContext.Current.User.Identity.IsAuthenticated) { string[] rolelist = GetRoleListForUserFromAPI(User.Identity.Name); HttpContext.User = new GenericPrincipal(User.Identity, rolelist); } } 

It's invoked after forms authentication is finished with it's processing.

http://msdn.microsoft.com/en-us/library/ff647070.aspx

Update

I had the wrong method signature (just checked in one of my own applications).

8 Comments

Brb, off to do some testing on this :)
@jgauffin - this looks interesting, but I'm wondering how this information get's persisted on subsequent requests.
Looks like it's persistent here, putting IsAdmin: @User.Identity.IsInRole("Admin") on the Master.cshtml is saying True on each page.
@ek_ny: Authentication is never stored. It's always loaded for each request. You could go about and use the session in PostAuthenticate if you want.
Yes, my issue with this is that it's being called on every single request, including requests to things like javascript files and images.
|
1

My first thought is that you should investigate implementing a custom role provider. This might be overkill but seems to fit in with the role-based plumbing.

More info from MSDN here.

1 Comment

This isn't actually a bad approach, although daunting as it might seem at first. It was fairly simple to create a class, inherit from RoleProvider and implement a couple of the methods to call the API to get role information. Some simple caching in the middle and it seems to work rather well. Added bonus is that I can pull into a separate dll and distribute via nuget to all apps that want to authorize against this particular API.
1

Much to the aghast of some, the session object ISNT a bad idea here. If you use temp data, you already take a hit for the session.

Storing this data in the cookie, well - Forms auth tokens have already been exploited in the POET vulnerability from a year and a half ago, so in that case someone could've simply formed their own cookie with the "admin" string in it using that vulnerability.

You can do this in post authenticate as @jgauffin mentioned. If the session state isn't available there you can use it then in Application_PreRequestHandlerExecute and check it there.

If you want to check if session state is available in either see my code at: How can I handle forms authentication timeout exceptions in ASP.NET?

Also whenever using forms auth and sessions, you always want to make sure the timeouts are in sync with each other (again the above code)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.