I'm using asp.net and trying to assign roles for a user with forms authentication like this:
public ActionResult AdminLogin(string password, string username)
{
User _user = _us.GetUsers(username, password).FirstOrDefault();
if (_user != null)
{
string _username = _user.Username;
FormsAuthentication.SetAuthCookie(_username, false);
string[] _roles = _us.GetUserRoles(_username);
HttpContext.User = new GenericPrincipal(HttpContext.User.Identity, _roles);
return RedirectToAction("Index", "Admin");
When I debug HttpContext.User.Identity always is null, but _username and _roles contains the proper data. Howto fix this?
/M
Your action is setting the User IPrincipal for the current context. As soon as you redirect to your other action (and all subsequent requests) a new HttpContext is created with a null User IPrincipal.
What you could do is persist the information in the authentication cookie and then extract that data in the Application_AuthenticateRequest method in your Global.asax file and set the User property of the HttpContext there.
This answer contains more details and example code
I believe the issue is that you are just setting the user as authenticated, and therefore, the HttpContext is not updated yet since the auth cookie has not yet been set on the users side of the request.
I was struggling too.
I was trying to carryout my authentication and authorization inside a WCF service using standard ASP.Net Membership and Role providers.
I wanted to pass in credentials and a 'requested app' to determine if the user 'authenticated' for that app. (not the ASP.Net APP, but an app in my own database).
To do this, I wanted access to the roles, but didn't want to 'redirect' or have a second call to my WCF service.
Here is some code that works for me:
First I determine if the user is valid as follows:
if (Membership.ValidateUser(CompanyCn, CompanyPwd))
{
sbLogText.AppendFormat("\r\n\r\n\tValid User UID/PWD: '{0}'/'{1}'", CompanyCn, CompanyPwd);
FormsAuthentication.SetAuthCookie(CompanyCn, false);
}
Then the following code workes nicely for getting the list of roles:
List<string> roleList = new List<string>(Roles.GetRolesForUser(CompanyCn));
sbLogText.AppendFormat("\r\n\r\n\tUser ('{0}'): Roles ({1}):", CompanyCn, roleList.Count);
foreach (string s in roleList)
sbLogText.AppendFormat("\r\n\t\tRole: {0}", s);
Related
I am building an intranet application using ASP.NET MVC 4 with Windows authentication. In the global.asax file, I have implemented this method:
protected void WindowsAuthentication_OnAuthenticate(object sender, WindowsAuthenticationEventArgs args)
In this method, I create a new ClaimsIdentity and set args.User to it, just like the example on MSDN. Later on in the application, in one of the Controllers, I need to get some data from the database. Since I already had an API action that does this, I call that API (synchronously) from my Controller.
The API gets the claims for the current user using the ApiController.User property. Here though, the claims are not the ones I set in global.asax. In fact, they are the claims that were in place on the user before this request.
The strange thing (to me) is that the next time I make a call to the application, the new claims are in place. So in my case, I change the claims that later on decide which buttons should be visible to a user, but only after the user makes another request to the application, these buttons are updated.
How can I make sure that the claims that I set in global.asax immediately take effect?
Extra info:
I don't set the claims on every request. When this method executes, I check a number of things to see if the user is still valid: cookie, user isn't anonymous, and user is still "valid". The latter is decided by cache - I keep a list of users that are still valid and if someone updates their permissions through a user interface, they become invalidated and will receive new claims in their next request.
I've attached a debugger and I see my code getting executed, the principal gets all the claims I want it to have while still in this method. When I reach a controller action, ApiController.User has the claims it had on the request before this one. When I make another request, the authentication method is skipped (because the user name is now in the cache), and in the controller the ApiController.User has the correct claims.
You need to set both the members to make it work.
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
I don't think you can access your claims in the same request that you set them. Try to redirect after setting your claims.
I'm doing something similar. Here is my code, i hope it would be helpful.
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var clamisIdentityBuilder = DependencyResolver.Current.GetService<IClaimsIdentityBuilder>();
var transformer = new ClaimsTransformer(clamisIdentityBuilder);
var principal = transformer.Authenticate(string.Empty, ClaimsPrincipal.Current);
// user if authenticated but Claims could not be created (they are not available in cache nor DB)
if (principal == null)
{
var cacheProvider = DependencyResolver.Current.GetService<ICacheProvider>();
cacheProvider.Clear();
FormsAuthentication.SignOut();
Response.Clear();
string redirectUrl = FormsAuthentication.LoginUrl;
Response.Redirect(redirectUrl);
}
else
{
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
}
}
I have a Winform client that we are slowing changing inline SQL data calls into ASP.NET Web API calls. We currently use the WindowsPrincipal.IsInRole check in the Winform client to determine if the user can run the SQL data calls. We would like to move into a Claims type setup where both the Winform client and the Web API can check the roles "claims" of a user.
I can't seem to find any "good" articles on how to get a Winform client to (1. Pass the claim to the service) and (2. Use a claim check inside the Winform client like the IsInRole). Any help or push in the right direction would be great.
--EDIT
So I used this article http://zamd.net/2012/05/04/claim-based-security-for-asp-net-web-apis-using-dotnetopenauth/ as a sample on getting a token back from the server but the article does not show how to get the claims identity out of the http client. Any idea how to get the claims identity out of the http client?
While I haven't tested this code, hopefully it will get you moving in the right direction.
I believe to answer your question you do this in your ClaimsAuthenticationManager where upon validating the token received from the server you set the Thread.CurrentPrincipal -- the same way you do on the web side without setting the HttpContext.Current.User principal.
Again this isn't tested but I think it would look something like this...
In my Token Validator I have the following code:
public static ClaimsPrincipal ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(new JwtSecurityToken(token),
Constants.TokenValidationParameters);
return FederatedAuthentication.FederationConfiguration
.IdentityConfiguration
.ClaimsAuthenticationManager.Authenticate(token, claimsPrincipal);
}
public static string GetToken(string username, string password)
{
OAuth2Client client = Constants.OAuth2Client;
AccessTokenResponse response = client.RequestAccessTokenUserName(username.ToLower(), password,
Constants.AllowedAudience);
return response.AccessToken;
}
Within my ClaimsAuthenticationManager I have modified the following code as you don't want to set the HttpContext in a non web environment:
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
/* HttpContext.Current.User = */ Thread.CurrentPrincipal = incomingPrincipal;
return incomingPrincipal;
}
I believe you then just have to set the appropriate keys in the app.config, specifically the system.identityModel => identityConfiguration => claimsAuthenticationManager
Once the thread you are running on has the "Authenticated Principal" you should be able to call the ClaimsPrincipal.Current.HasClaim() or your higher level Authorization.CheckAccess() function to validate sections of your WinForm logic.
Hope this helps :)
I'm using the Forms Auth and ASP Universal Membership Provider in an MVC 3 Site. We're persisting the cookie for user convenience.
FormsAuthentication.SetAuthCookie(model.UserName, true)
When we disable a user in the Membership provider like this:
memUser.IsApproved = model.IsActive;
provider.UpdateUser(memUser);
if they have the cookie they can still get in to the site. This is similar to what is described in this post:http://stackoverflow.com/questions/5825273/how-do-you-cancel-someones-persistent-cookie-if-their-membership-is-no-longer-v
We use Authorize attributes on our controllers, and I know that that is technically more Authorize than Authentication. But the certainly overloap so I'm trying to figure out what is the best MVC way to do a check that the user is not actually disabled? Custom AuthorizeAttribute that checks the user against the membership database? An obvious setting/method I'm missing with Forms auth to invalidate the ticket?
Update:
Here’s basically what I'm going with – we use a custom permission denied page which we we use to better inform user that they don’t have rights vs. they’re not logged in. And I added the IsApproved check. AuthorizeCore gets called when you put the attribute on a Controller or Action and if it returns false HandleUnauthorizedRequest is called.
public class CustomAuthorization : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated || !Membership.GetUser(filterContext.HttpContext.User.Identity.Name).IsApproved)
{
filterContext.Result = new HttpUnauthorizedResult();
// in the case that the user was authenticated, meaning he has a ticket,
// but is no longer approved we need to sign him out
FormsAuthentication.SignOut();
}
else
{
var permDeniedRouteVals = new System.Web.Routing.RouteValueDictionary() { { "controller", "MyErrorController" }, { "action", "MyPermissionDeniedAction" }, { "area", null } };
filterContext.Result = new RedirectToRouteResult(permDeniedRouteVals);
}
}
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
// since persisting ticket,
// adding this check for the case that the user is not active in the database any more
return base.AuthorizeCore(httpContext) && Membership.GetUser(httpContext.User.Identity.Name).IsApproved;
}
}
Usage:
[CustomAuthorization()]
public class MyController
Well, you're going to have to check the database regardless, the only question is how you want to do that. Yes, you could create a custom authorize attribute, or you could write some code for the OnAuthorize override in ControllerBase, or you could do it in Application_AuthenticateRequest.. lots of ways you could do it, depends on what works best for you.
The best way, of course, would be to not use a persistent ticket if this is an issue for you.
I pretty much always use Roles and a RolesProvider, even if there is just one role named "Login" - in part for this issue. This way, your Authorize attributes might look something like this:
[Authorize(Roles="Login")]
Where Login represents a basic 'Role' that all "active" accounts must have to be able to log in at all; Every protected action is protected by this, at minimum.
That way, simply removing the "Login" role effectively disables the user... because, in my Roles Provider, I am checking the logged-in user's roles against the database or server-local equivalent.
In your case, your "Login" role could simply resolve to a check on the IsApproved field on your user model.
I am creating asp.net MVC Application using MVC 3.0. I have 2 users but the DataBase is the same. So, Is it possible to setup two connection strings or even more in web.config? when user login, I redirect it to his database, so then he can use his DataBase.
So major issue here is to find out which user is logged in and use connection string for that user.
I am using default mvc account controller and for example when i want to display welcome message for user in my view i type: if (#User.Identity.Name == "UserName") then some message
So where is the best place to find out which user is logged in and set his connection string in controller or in a view?
Yes, you can have as many connection strings in your web.config file as you want.
But, if you're designing a multi-tenant application than there are better ways of doing it than adding a connection string to web.config file every time a new user signs up.
Probably the best way for you is to have a single database where user-related tables have foreign keys to Users table.
You can learn more about multi-tenant architectures from this Microsoft article.
I agree with Jakub's answer: there are better ways of handling multi-tenancy than having a different database per user.
However, to answer your specific question, there are two options that come to mind:
You can set the connection string to a session variable immediately after login.
Your data access layer can choose the connection string based on the logged in user when it's created. (I'd recommend this over the first option)
To store the connection after login, if you're using the standard ASP.NET MVC Account Controller, look at the LogOn post action:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
//EXAMPLE OF WHERE YOU COULD STORE THE CONNECTION STRING
Session["userConnectionString"] = SomeClass.GetConnectionStringForUser(model.UserName);
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
If you wanted to select the connection string when doing data access, your repository or data access layer will probably have a system for handling that. For instance with Entity Framework Code First, the DbContext constructor allows you to pass in the name of a connection string when you're creating it:
connectionString = SomeClass.GetConnectionStringForUser(model.UserName);
DbContext context = new DbContext(connectionString);
But again, I'd look at other ways of handling multitenancy unless your business dictates that your users have physically separate databases.
you can have multiple connection strings in web.config. Now if you want to use different connection string for different users there must be some criteria for division of users
<appSettings><add key="connectionString" value="Data Source=develope\sqlexpress;Initial Catalog=validation_tdsl;Integrated Security=True;Max Pool Size=1000;Connect Timeout=60000;"></add>
<add key="connectionString1" value="server=MARK\SQLEXPRESS;database=name;integrated security=true;Max Pool Size=1000;Connect Timeout=60000;"></add>
<add key="connectionString2" value="server=name\SQLEXPRESS;database=FM;integrated security=true;Max Pool Size=1000;Connect Timeout=60000;"></add>
and later you can use them like following
Dim con As New SqlConnection(System.Configuration.ConfigurationSettings.AppSettings("connectionString"))
Dim con1 As New SqlConnection(System.Configuration.ConfigurationSettings.AppSettings("connectionString1"))
EDIT : In c# it would be:
SqlConnection con = new SqlConnection(System.Configuration.ConfigurationManager.AppSettings["connectionString"]);
SqlConnection con1 = new SqlConnection(System.Configuration.ConfigurationManager.AppSettings["connectionString1"])
Note: ConfigurationSettings is now obsolete.
I am upgrading a site to use MVC and I am looking for the best way to set up Authentication.
At this point, I have the log-in working off of Active Directory: validating a username and password, and then setting the Auth cookie.
How do I store the user's role information at time of log-in, in order for my controllers to see those roles as the user navigates through the site?
[Authorize(Roles = "admin")]
I have no problem getting a list of roles from Active Directory. I just don't know where to put them so that the controllers will see them.
Roles are added to the IPrincipal of the HttpContext. You can create a GenericPrincipal, parse the list of roles in the constructor and set it as HttpContext.User. The GenericPrincipal will then be accessible through User.IsInRole("role") or the [Authorize(Roles="role")] attribute
One way of doing this (in C#) is to add your roles as a comma separated string in the user data parameter when creating your authentication ticket
string roles = "Admin,Member";
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
userId, //user id
DateTime.Now,
DateTime.Now.AddMinutes(20), // expiry
false, //do not remember
roles,
"/");
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(authTicket));
Response.Cookies.Add(cookie);
Then access the role list from the authentication ticket and create a GenericPrincipal from your Global.asax.cs
protected void Application_AuthenticateRequest(Object sender, EventArgs e) {
HttpCookie authCookie =
Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null) {
FormsAuthenticationTicket authTicket =
FormsAuthentication.Decrypt(authCookie.Value);
string[] roles = authTicket.UserData.Split(new Char[] { ',' });
GenericPrincipal userPrincipal =
new GenericPrincipal(new GenericIdentity(authTicket.Name),roles);
Context.User = userPrincipal;
}
}
When you authenticate your user, you generate a new GenericPrincipal instance. The constructor takes an array of strings which are the roles for the user. Now set HttpContext.Current.User equal to the generic principal and write the auth cookie, and that should do it.
For those of you using MVC 4 or Greater you will need to take Jaroslaw Waliszko's advice when making use of David Glenn's answer:
"I've tested it in ASP.NET MVC 4 and I suggest to use Application_PostAuthenticateRequest instead. Otherwise the generic principal will be overridden." – Jaroslaw Waliszko Sep 7 at 16:18
So as stated above, all you need to do is replace the Application_AuthenticateRequest method name with Application_PostAuthenticateRequest to get this to work. Worked like a charm for me! If I was allowed to upvote Jaroslaw and David, I would.
I'd be inclined to just create a custom role provider. Example here:
http://www.danharman.net/2011/06/23/asp-net-mvc-3-custom-membership-provider-with-repository-injection/
Could you not drop in either an authorization store role manager or find (e.g. on Codeplex) or write another Role Provider that works with Active Directory to get the groups information?
This would save you the hassle of authenticating the user, getting their roles, and then re-passing that information into the constructor, and would all happen automatically for you as part of the framework.