Using IPrincipal in with MVC3 AuthorizationAttribute and avoiding RoleProvider - c#

I have a WCF service that returns an an object that implements both IPrinciple and IIdentity.
I assumed that somehow I could hook this up to the MVC3 authorization system without having to create a RoleProvider
e.g. so I could do something like this in my AccountController logon method:
// AthenticatedUser implments both IPrinciple and IIdentity
AthenticatedUser user = wcfService.Logon(password, userName);
FormsAuthentication.SetAuthCookie(userName, false);
// Set IPrinciple so I can use IsInRole method elsewhere (or AuthorizationAttribute can reuse it)
this.HttpContext.User = authenticationClient.AuthenticatedUser;
Then by some magic when I use
[Authorize (Roles = "foo", "bar")]
the IsInRole method of my AuthenticatedUser gets called.
However in my testing / debugging I have found that this.HttpContext.User does not seem to maintained across requests.
Edit sorry: I should have made it clear that I don't want to have to call my WCF service on each request, I'd like to somehow cache / store the user and roles and be able to use the AuthorizeAttribute with the IPrinciple comeing from my service.
Can anyone help? Thanks in advance!

If you are using IIS 7 to run this on your local machine add this to web.config under system.webServer:
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
<remove name="UrlAuthorization" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<remove name="DefaultAuthentication" />
<add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
<remove name="RoleManager" />
<add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
</modules>

You say "this.HttpContext.User does not seem to maintained across requests" - that is the correct behavior. Each request has a unique HttpContext.
What you might want to try is this:
FormsAuthentication.SetAuthCookie(userName, true);
, which creates a durable cookie that lasts between browser sessions.
http://msdn.microsoft.com/en-us/library/bk50ykcd.aspx

As you have found out, the HttpContext is not persisted across requests and hence neither are IPrincipal and IIdentity - the HttpContext is constructed at the start of each request by the framework, and the IPrincipal and IIdentity are constructed from the authentication ticket deserialised from the authentication cookie.
What you are describing sounds similar in some ways to a WCF authentication service. In this scenario, the service will authenticate the user and send back an authentication cookie in the response to the calling application, and that application will then use the cookie to construct the IPrincipal and IIdentity on each subsequent request.
You can override the IPrincipal and IIdentity when the Application.PostAuthenticateRequest event is raised
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
if (Context.User != null)
{
var identity = Context.User.Identity;
// define our own IIdentity and IPrincipal for an authenticated user
if (identity.IsAuthenticated)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
// get the roles from somewhere
var roles = GetRoles();
identity = new CustomIdentity(ticket.Name);
IPrincipal principal = new CustomPrincipal(identity, roles);
Context.User = Thread.CurrentPrincipal = principal;
}
}
}
You can see that the roles need to be retrieved from somewhere. With a RoleProvider in place, these roles can be cached in another cookie. If you know what you're doing in terms of security, you could look at replicating how the roles are serialized and encrypted in the cookie.
An alternative may be to store the roles in session and may be adequate for a small number of roles. Bear in mind that session is not available until the PostAcquireRequestState event has been raised, some 7 events later in the application request lifecycle than PostAuthenticateRequest.

It is possible to react in your global asax on the AuthenticateRequest event and change the IIdentity like this:
public class MvcApplication : System.Web.HttpApplication
{
public override void Init()
{
base.Init();
this.AuthenticateRequest += new EventHandler(MvcApplication_AuthenticateRequest);
}
void MvcApplication_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Request.IsAuthenticated)
{
var name = HttpContext.Current.User.Identity.Name;
var key = "User-" + name;
var principal = HttpContext.Current.Cache["User-" + name];
if (principal == null)
{
principal = GetYourUserAsIPrincipal();
// Add to cache for 1 hour with sliding expiration
HttpContext.Current.Cache.Insert(key, principal, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(1, 0, 0));
}
HttpContext.Current.User = principal ;
}
}
}
EDIT: For caching you can use the default ASP.NET Caching. I've edited the above sample. Note, the ASP.NET cache is thread safe (http://msdn.microsoft.com/en-us/library/system.web.caching.cache.aspx)
It would be the best, if you wrap all of your cache access logic into a single class.
As with any caching mechanism: If you change your users roles, it will be reflected only when the cache has been expired.

Related

Mvc C# session lost after multiple login

I'm creating a simple website for multiple users. Now i'm trying to make the profile of each one so when they login they can see their data. The problem is that once logged in with one user and i login with another one my session gets "overwritten" and the new user become the old one. So i think i'm messing up with the session part.
I'm telling you what i've done.
Inside the login controller i call a the Login function, passing username and password.
Then i validate the user credentials to the database and if the user has been authenticated i do:
new_session = new UserSession();
new_session.SessionSet(obj);
Inside the "SessionSet" i just initialize some variables i need:
this.id_account = obj.id_account;
this.id_user = obj.id_user;
this.username = obj.username;
At this point i return the new_session to the Controller and if it is not null i just add it to the session:
Session.Add("user", new_session);
The problem now is how can i get this specific object form : views, classes and controllers?
If i write in a view:
(((UserSession)Session["user"]).username)
I get only the "user" object so when 2 users log in at the same time, the session refer always to the "user" one.
I googled it but i can't find an answer that fits to my needs.
get database UserSession data
see user with this useID Is logged in
if user logged in {not allaw login} else {allow login}
or write data in session with timeout
if(Session["users"] != null)
{
List<UserSession> users = (List<UserSession>)Session["users"];
var finduser = users.Where(a=> a.id_user == newUser.id_user).FirstOrDefault();
if(findUser == null)
{
//add user into session
}
}
else
{
Session.Add("users",newUser);
}
and web.config file set timeout logout and session equal
<sessionState mode="InProc" timeout="20"></sessionState>
<authentication mode="Forms">
<forms loginUrl="/Pages/Default" timeout="20" slidingExpiration="true" />
</authentication>

How to read JWT from cookie and use it to Authorize the requests in MVC 5, C#? [duplicate]

I planned to use ASP.NET Identity 2.0 in an ASP.NET MVC application for authentication and authorization.
Referring the below link
JSON Web Token in ASP.NET Web API 2 using Owin
I was able to create a access token(JWT) for the valid user i.e., When user Logs in to the application I will validate the user with name and password then I will issue a JSON web token for that valid user.
Now, I read in some articles that we need to pass the bearer token in headers for every request to validate the user for authentication. In MVC we will provide Authorize attribute for the methods that needs to be protected as shown below…
public class UserController : BaseHRAppController
{
[Authorize]
public ActionResult Index()
{
return View();
}
}
How to tell my MVC application to use JWT for validating the user?
I want to make my MVC application validate the user using JWT whenever the user tries to access the method with authorize attribute. Since I will use AJAX calls in many pages to access method present in MVC controller, I don't think it's good to pass a token on every AJAX request. I need help to accomplish authentication and authorization in an efficient way using ASP.NET Identity in an MVC applicaton.
Currently, I don't know how to use this JWT token for authentication and authorization in an MVC application.
In order for MVC to understand anything about your JWT you basically have to tell it :-) . First, install the Jwt package from nuget:
Install-Package Microsoft.Owin.Security.Jwt
Then open up your Startup.cs file and add a new funtion that will tell MVC how to consume JWT. At basics your Startup will look something like:
using System.Configuration;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Jwt;
using Owin;
[assembly: OwinStartupAttribute(typeof(TOMS.Frontend.Startup))]
namespace TOMS.Frontend
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ConfigureOAuthTokenConsumption(app);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["Issuer"];
var audienceId = ConfigurationManager.AppSettings["AudienceId"];
var audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
}
}
You will notice that I am placing the issuer, audienceId and audienceSecret in my Web.config file. (Those values should match the ones on your Resource Server). Also, you might want to ensure you have an updated System.IdentityModel.Tokens.Jwt running:
Update-package System.IdentityModel.Tokens.Jwt
With those set, you may decorate your controller Action with the [Authorize] attribute and play ball.
Play ball of course meaning fire your request from your javascript to your protected controller action:
//assuming you placed the token in a sessionStorage variable called tokenKey when it came back from your Authorization Server
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
headers.Authorization = 'Bearer ' + token;
}
$.ajax({
type: 'GET',
url: 'CONTROLLER/ACTION',
headers: headers
}).done(function (data) {
self.result(data);
}).fail(showError);
UPDATE
By The way, if you wish to add the values in your web.config file in order to retrieve them as I did above; simply add them under the AppSettings:
<configuration>
<appSettings>
<add key="Issuer" value="YOUR_ISSUER" />
<add key="AudienceId" value="YOUR_AUDIENCEID" />
<add key="AudienceSecret" value="YOUR_AUDIENCESECRET" />
</appSettings>
</configuration>
...of course, replacing the "values" with your own
I don't know if you solved this, but I was having a similar issue and decided to store the token using FormsAuthentication which I was able to encrypt the token, and on each request the cookie was passed back and then I could decrypt it to get the JWT and then pull out the roles/claims from and then use those roles to create and Identity Principal that would allow me to decorate my controller methods with [Authorize(Role="blah,blah")].
Here is some sample code below.
Once you get the JSON web token back from the api after login, you can use something like:
var returnedToken = (TokenResponse)result.ReturnedObject;
var ticket = new FormsAuthenticationTicket(1, model.Email, DateTime.Now, ConvertUnitToDateTime(returnedToken.expires_in), true, returnedToken.access_token);
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.HttpOnly = true;
Response.Cookies.Add(cookie)
I have some of my own created classes and methods in there, but it will give you the general idea that you store the JWT access token as well as the expiration date in your FormsAuthentication cookie.
Then the cookie is passed with each request and in your Global.asax file you can have a method to authenticate the request:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
//Extract the forms authentication cookie
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JwtSecurityToken jwTok = TokenHelper.GetJWTokenFromCookie(authCookie);
// Create the IIdentity instance
IIdentity id = new FormsIdentity(authTicket);
// Create the IPrinciple instance
IPrincipal principal = new GenericPrincipal(id, TokenHelper.GetRolesFromToken(jwTok).ToArray());
// Set the context user
Context.User = principal;
}
}
So that method you would decrypt the cookie to get the JWT access token which you can then decode using the System.IdentityModel.Tokens.Jwt library from Microsoft and then take those roles and ID and generate the principal and identity for the user which creates your user with the roles.
Then those roles can be validated against the [Authorize] attribute.

Custom RoleProvider with Windows Authentication in Web API

I have a WebAPI project in which I need to use Windows Authentication for the Users but implement a Custom Role Provider for the Authorize attributes above the routes. When I implement it however I always get {"Message":"Authorization has been denied for this request."} as the result for my call. Furthermore none of my breakpoints ever trigger except for the one in the constructor of the Custom Role Provider
Controller
[Authorize(Roles="Administrator")]
[RoutePrefix("api/Constituents")]
public class ConstituentsController : ApiController
{
[Route("Constituents")]
[HttpGet]
public IDataResponse<List<IConstituent>> GetConstituents()
{
return service.GetConstituent();
}
Custom Role Provider
public class CustomRoleProvider : RoleProvider
{
public CustomRoleProvider()
{
this.UserRepo = new UserRepository(); //Breakpoint here triggers
this.RoleRepo = new RoleRepository();
}
public override string[] GetRolesForUser(string username)
{
var roles = UserRepo.GetUser(username)?.Roles?.Select(r => r.Name).ToArray();
return roles;
}
public override bool IsUserInRole(string username, string roleName)
{
var user = UserRepo.GetUser(username);
return user.Roles.Select(r => r.Name).Contains(roleName);
}
Web Config
<authentication mode="Windows"/>
<roleManager cacheRolesInCookie="false"
defaultProvider="CustomRoleProvider"
enabled="true">
<providers>
<clear />
<add name="CustomRoleProvider"
type="Data.CustomRoleProvider, Data" />
</providers>
What piece of the puzzle am I missing? I need to get the current user making the request and then check to see if the have the appropriate role in the database.
Thanks
By default, Visual Studio sets the Windows Authentication property on your project to Disabled. In the Properties pane (not the tab), you need to flip the property to Enabled. That should let you hit the breakpoints on your RoleProvider.
When you put your application on a server, you may have to perform a similar process in IIS to Enable Windows Authentication and Disable Anonymous Authentication.

ASP.NET Identity + Windows Authentication for Intranet Site

I am building an intranet site where users will be on the corporate domain and have different permission levels. I'm currently using <authentication mode="Windows"/> to control site access, but it seems like I should be using ASP.NET Identity.
For example, say my application is a dashboard for each department in the organization. I want to create a single AD group called DashboardUsers and put everyone that can touch the site in this group.
I also want to restrict the views in the dashboard. For example, I only want the IT group to see their view, and the Finance folks see theirs, etc.
Question -- should I be using Windows Authentication to control access to the site, and then use ASP.NET Identity for user level permissions?
I have done something similar to this using only WindowsAuthentication. You can tag your actions with the Authorize Attribute:
[Authorize(Roles = #"DashboardUsers")]
As long is this user is a member of the DashboardUsers AD group they will get access to this action. It seems like MVC magic, but it really is that simple.
Unfortunately this approach will not allow you to overload an action for different Roles as the Authorize Attribute is not part of the method's signature. In your views, you would have to show different anchor tags based on the current users role.
ie:
[Authorize(Roles = #"DashboardUsers\Manager")]
public ActionResult IndexManagers()
{
..
}
or
[Authorize(Roles = #"DashboardUsers\Finance")]
public ActionResult IndexFinance()
{
..
}
EDIT AFTER COMMENT:
Since your Identity is coming from AD, you could use logic in your controller like:
if(User.IsInRole("Finance"))
{
..
}
else if(User.IsInRole("IT"))
{
..
}
And this will check which AD Group they belong too. I know it's not very elegant, but I can't imagine mixing Windows Auth with a custom identity and managing permissions in your own db would be elegant either.
I have run into this dilemma before and wound up creating a custom role provider that i used in conjunction with windows authentication. I'm not sure you need the OWIN middleware when authenticating against AD.
public class MyAwesomeRoleProvider : RoleProvider
{
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
// i talk to my database via entityframework in here to add a user to a role.
}
// override all the methods for your own role provider
}
config file
<system.web>
<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="MyAwesomeRoleManager">
<providers>
<clear />
<add name="MyAwesomeRoleManager" type="MyAwesomeNamespace.MyAwesomeRoleProvider" connectionStringName="MyAwesomeContext" applicationName="MyAwesomeApplication" />
</providers>
</roleManager>
</system.web>

How to Custom Authorization from database

Normally use authorization settings to interact with the roles.
<location path="Register.aspx">
<system.web>
<authorization>
<allow roles="Administrator"/>
<deny users="?"/>
</authorization>
</system.web>
</location>
But I would like to have this setup in the database in a table
tblAuthorization
-IdAuthorization (Identy(1,1))
-IdCompany=5
-IdRol=5 (Administrator”)
-Path=”Register.aspx”
Is there a setting for a class to do this? There is something like Profile, RoleProvider ..
<roleManager enabled="true" defaultProvider="MyAccessRolProvider">
<providers>
<clear />
<add name="MyAccessRolProvider" type="MyAccessRolProvider" connectionStringName="MyConnectionString" applicationName="/" />
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
</providers>
</roleManager>
Right now the only I think that can to implement a validation in the Page_Load event
And if it is invalid making a Redirect but I would do a little more "Professional"
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if(!ValidateAuthorization())
{
Response.Redirect("Login.aspx");
}
}
}
Could help me with examples?
Thank you very much in advance
Your best bet is to implement a custom RoleProvider - you seem to be heading in this direction as your question includes a configuration section with a custom RoleProvider (MyAccessRolProvider).
You need to specify the fully-qualified name of your RoleProvider, e.g. MyNamespace.MyAccessRoleProvider, MyAssembly.
MSDN describes how to do this, including a sample implementation.
With the understanding that you are taking about authorization at the page level you could add a HTTP module (IHttpModule) and use your module to do the authorization checking. If the authorization failed then you could redirect as appropriate.
Something like this might work:
public class MyAuthorizationModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.AuthorizeRequest += (new EventHandler(AuthorizeRequest));
}
private void AuthorizeRequest(Object source, EventArgs e)
{
bool isAuthorized = //your code here to check page authorization
if (!isAuthorized)
{
var response = //will need to get current response
response.Redirect("Login.aspx", true);
}
}
}
Note: Don't forget you'll need to add the module to your web application.
There is no out of the box support for this as far as I know however I would suggest you implement something as follows:
Create a new class that derives from AuthorizeAttribute, add an ID property and override the "AuthorizeCore" method.
public class CustomAutorizeAttribute : AuthorizeAttribute
{
public Guid ActionId { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Check user has access to action with ID = ActionId
//implementation omitted
}
}
You can add a new database table to hold on the Actions and their associated ID values. You can either add the record to the table manually or write some code to do this using reflection (I would advise this if you have quite a few actions and are likely to add more in the future).
Once this is done you can assign users or application roles access to these actions.
In you custom Authorize attribute you have to check whether the user has access to the given action returning true or false accordingly from the AuthorizeCore method.
I would advise grabbing a collection of all actions a user has access to when a user logs in and storing them in either the server cache or some kind of distributed cache so that you aren't hitting your database on every request.
UPDATE - Sorry just realised you're using ASP.NET Forms and not MVC. My approach is for an MVC application so its probably not relevant although I'll leave it up here in case any MVC developers happen to stumble across your question.

Categories

Resources