I have an Asp.Net MVC 5 application. I want to use user roles to authorize only some people to use a specific action. I've changed my Web.config file like this:
<roleManager enabled="true"/>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<clear/>
<add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
</providers>
</membership>
This is how I add users to roles:
if (!await Roles.RoleExists(role))
await Roles.CreateRole(new Role(role));
await Roles.AddUserToRole(role, user.Id);
Currently I am able to access the roles for a user through code using something like this:
public static async Task<IEnumerable<string>> GetUserRoles(string username)
{
if (string.IsNullOrEmpty(username))
return new string[0];
string userId = await Logins.GetUserId(IdentityConfig.LocalLoginProvider, username);
var roles = await Roles.GetRolesForUser(userId);
return roles;
}
However, when I try to use the Authorize attribute to access the roles, the page will get stuck and nothing loads.
[Authorize(Roles = "Admin")]
public ActionResult Index()
{
return View(db.Restaurants.ToList());
}
What am I doing wrong here?
Update:
After a while the webpage will show this error:
You cannot really mix the old membership/roles with the new identity system. You need to pick one or the other. The authorize attribute should work fine assuming you added the user to the admin role via the new identity apis.
At the bottom, this article demonstrates roles Mvc5 tutorial
I had the same problem, it has nothing to do with your connection string. Add the following to your web.config:
Here's the answer: http://blog.luppes.com/2013/12/08/mvc5-authentication-roles-failure/
As of ASP.NET 4.5.1/VS2013, MVC as of MVC5 is no longer a separate product, but is now completely integrated into ASP.NET. This is part of Microsoft's new One ASP.NET strategy.
In addition to this, Microsoft has developed a new kind of identity management system called the ASP.NET Identity, which does not use the legacy membership system.
Basically, if you want to use the old membership system, you need to use MVC4 or earlier. If you want to use ASP.NET Identity, then MVC5.
Related
I have an existing Asp.Net 4.0 web application that uses forms authentication. I want to add Windows authentication for domain users. I have read several articles and tried a few different approaches, but have met with various problems.
Here are the best 2 examples I have found that are relevant to IIS 7 and higher.
http://mvolo.com/iis-70-twolevel-authentication-with-forms-authentication-and-windows-authentication/
and
http://devmemass.blogspot.com/2012/02/mixing-forms-and-windows-authentication.html
The short story is that neither of these approaches have worked for me. Does anyone have a better (and especially more thorough) example?
I really only need NTLM / Kerberos authentication handled. I don't want passwords passed in clear text for domain users. I would like IIS to handle the Windows authentication part, then allow me to redirect them to the default page.
I have done this successfully with my applications. If you want to use have the sign on with Windows Authentication, it's pretty easy. If you want to be able to use your Active Directory groups, it's a little more difficult. To just use Forms Authentication with AD you will need the following in your webconfig...
<connectionString>
<add name="ADService" connectionString="LDAP://domain/OU=ougroup,DC=domain,DC=net" />
</connectionString>
Then you will need Membership provider.
<membership defaultProvider="AspNetActiveDirectoryMembershipProvider">
<providers>
<clear />
<!--Membership provider for Active Directory-->
<add name="AspNetActiveDirectoryMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ADService" attributeMapUsername="sAMAccountName" />
</providers>
</membership>
This should do it for you.
If you want to be able to use your AD groups, you will need to create your own RoleProvider.
I used this link here.
However, I did have to change the GetRolesForUser to..
public override string[] GetRolesForUser(string username)
{
List<string> allRoles = new List<string>();
var ctx = new PrincipalContext(ContextType.Domain, "jwaf-dc2.jeffwyler.net");
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);
if (user != null)
{
var groups = user.GetGroups();
allRoles.AddRange(groups.Select(x => x.Name));
}
return allRoles.ToArray();
}
I hope this helps get you started.
I am creating an ASP.NET MVC 4 web application. I googled about custom membership, but I couldn't find good resources or video lectures.
Most of them are either outdated or dead links. Please could you suggest some resources about how to start writing a membership and role provider.
Understanding about membership and roles was pretty difficult for me too, as you said there are not many reliable and detailed content you will find on web. I tried watching several videos to Understand about this topic but wasn't clear. But then two articles from a website called Code Project came for the rescue. I am sharing these Link where you can see a step by step guide about customize membership
Link 1
The link 1 will help you to replace an email with username for login authentication this is one of the most common customization the developers need in the microsoft provided Identity Module.
Link2
The second article will help you understand adding and attaching roles to the created user and how to limit the access of user registration page to an Admin only. This way with the help of these two articles I hope that you will Understand the Basics of Authentication and Authorization.
I suggest using ASP.Net Identity instead of old membership.ASP.Net Identity is a way better and more flexible than old membership, it also supports role-based authentication using action filters and you can implement your own customized providers (such as role and user providers).
see links below
https://weblog.west-wind.com/posts/2015/Apr/29/Adding-minimal-OWIN-Identity-Authentication-to-an-Existing-ASPNET-MVC-Application
http://www.c-sharpcorner.com/article/create-identity-in-simple-ways-using-asp-net-mvc-5/
The ASP.NET MVC 4 Internet template adds some new, very useful features which are built on top of SimpleMembership. These changes add some great features, like a much simpler and extensible membership API and support for OAuth. However, the new account management features require SimpleMembership and won't work against existing ASP.NET Membership Providers
Check out resources for ASP.NET Identity here:
http://www.asp.net/identity/overview/getting-started/aspnet-identity-recommended-resources
http://logcorner.com/how-to-configure-custom-membership-and-role-provider-using-asp-net-mvc4/
**for creating a CustomerMemberShipClass** your class must implement System.Web.Security.MembershipProvider abstarct class. and you override the method ValidateUser()
in this ValidateUser() you have to write your own logic based on which you want authenticate user and return true or false according to it.
Sample ValidateUser method
public override bool ValidateUser(string username, string password)
{
int count=db.GetAll().Where(x => x.UserEmail == username && x.password == password).Count();
if (count != 0)
return true;
else
return false;
}
later in web.config file you have add the fallowing under <sytem.web> element
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear/>
<add name="MyMembershipProvider" type="Write your class name that is implementing membershipproviderclas"/>
</providers>
</membership>
after doing this you can validate user using **MemberShip.Validate(Username,password)** which returns true or false based on ur code in ValidateUser() in CustomMemberShipProvider class and this will also set **[Authorize] attribute**
**for creating a CustomRoleProviderClass** your class must inherit System.Web.Secuirty.RoleProvider and override the appropriate method to get the roles for the user
SAmple method for getting roles for user
public override string[] GetRolesForUser(string username)
{
string[] str={db.GetAll().Where(x=>x.UserEmail==username).FirstOrDefault().Role};
return str;
}
after this you must add the fallowing in web.config file in <system.web> element
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear/>
<add name="MyRoleProvider" type="BLL.DoctorAppointmentRoleProvider"/>
</providers>
</roleManager>
and after this u can check the role of the user using attribute **[Authorize(role="admin"])** and in Razor view you can check using User.IsinROle("A").
I am learning the migrations feature available in Entity Framework and I seem to hit a problem. When using an internet application project template and enabled entity framework migrations etc. I am trying to just create a default user onto the database - using an automatic migration:
protected override void Seed(eManager.Web.Infrastructure.DepartmentDb context)
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "Email", false);
if (!WebSecurity.UserExists("ahicks2"))
{
WebSecurity.CreateAccount("ahicks2", "password", requireConfirmationToken:true);
}
if (!Roles.RoleExists("Admin"))
{
Roles.CreateRole("Admin");
}
}
When I run "Update-Database -verbose" in the package manager I get the following error:
Running Seed method.
System.Web.Security.MembershipCreateUserException: The Provider encountered an unknown error.
at WebMatrix.WebData.SimpleMembershipProvider.CreateAccount(String
userName, String password, Boolean requireConfirmationToken)
at WebMatrix.WebData.WebSecurity.CreateAccount(String userName, String password, Boolean requireConfirmationToken)
The next line in the stack trace is the Configuration.Seed call. In my web config I am using the following setting for the membership provider to guarantee the tables are created in the database I want:
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<clear/>
<add name="DefaultMembershipProvider"
type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
connectionStringName="DefaultConnection"/>
The database tables are being created correctly, but the code falls over on the create user. Can anyone help? I'm sure its something simple. I wanted to test out these features - of course a simple answer is to revert back to using membership providers from asp.net 2.0! but I wanted to try and get this to work. Any help would be appreciated.
EDIT: By the way I've tried this: http://forums.asp.net/t/1598261.aspx/1 and it doesn't solve this problem.
The answer lies on the surface.
Take a look at the MSDN page regarding this method:
WebSecurity.CreateAccount Method
It presumes that matching user information (represented by userName) already exists in the user profile table. (To create a new user in both the membership table and in the user profile table, use the CreateUserAndAccount(String, String, Object, Boolean) method.)
This worked for me:
WebMatrix.WebData.WebSecurity.CreateUserAndAccount("YOURUSERNAME", "YOURPASSWORD");
You mght also wish to consider making sure the controller/class has
[InitializeSimpleMembership]
I'm trying to do a similar thing as you and struggling!
I'm failing with creating a role.
Dan.
I created STS that does the authentication part. It uses Custom Membership provider.
After successful login I get redirected to my RP website. All works fine in terms of authentication.
I have defined a CustomRolesProvider defined in web.config of my RP website. It uses the username returned by STS to fetch the roles for that user from RP's database.
When I use Roles.GetRolesForUser I do get the right roles.
I have the following in the web.config of my RP to allow only admin to give access to admin folder.
And the sitemap provider has securityTrimmingEnabled="true"
<location path="admin">
<system.web>
<authorization>
<allow roles="admin" />
<deny users="*" />
</authorization>
</system.web>
</location>
<add name="default" type="System.Web.XmlSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true" />
Problem:
When the user is in the admin role, the menu tabs for admin pages won't showup. I did check that Roles.IsUserInRole("admin") returns true. So the role is recognized by roles provider but not by authorization rules and sitemap provider in the web.config.
If I comment out the "location" from the web.config i.e. allowing every logged-in user to admin folder, my menu items show up fine.
From my understanding of WIF, RP can have it's own implementation of Roles and does not have to rely on Roles Claim from STS.
Does anyone has any ideas?
Update 2(01/20/2012): I found that the STS returns role claims as below:
http://schemas.microsoft.com/ws/2008/06/identity/claims/role = Manager
So if I change <allow roles="admin" /> to <allow roles="Manager" /> the role is picked up and menu tabs are shown appropriately.
So I am sure I am missing a link on how to make use of my roles and not the one returned via claims.
Update 2(01/20/2012):
If I add the role to the claimsIdentity like below it works:
void Application_AuthenticateRequest(object sender, EventArgs e) {
if (Request.IsAuthenticated) {
IClaimsPrincipal claimsPrincipal = HttpContext.Current.User as IClaimsPrincipal;
IClaimsIdentity claimsIdentity = (IClaimsIdentity)claimsPrincipal.Identity;
if (!claimsIdentity.Claims.Exists(c => c.ClaimType == ClaimTypes.Role))
{
claimsIdentity.Claims.Add(new Claim(ClaimTypes.Role, "admin"));
}
}
}
But then what would be the best place to add that code? If I add it in Application_AuthenticateRequest it's added upon each request and it keeps adding.(I fixed this by adding if statement)
*Update 3(01/24/2012):*Version 2 of my code that uses my CustomRoleProvider to get the Roles and then add it to the ClaimsCollection:
void Application_AuthenticateRequest(object sender, EventArgs e) {
if (Request.IsAuthenticated) {
string[] roleListArray = Roles.GetRolesForUser(User.Identity.Name);
IClaimsPrincipal claimsPrincipal = HttpContext.Current.User as IClaimsPrincipal;
IClaimsIdentity claimsIdentity = (IClaimsIdentity)claimsPrincipal.Identity;
var roleclaims = claimsIdentity.Claims.FindAll(c => c.ClaimType == ClaimTypes.Role);
foreach (Claim item in roleclaims)
{
claimsIdentity.Claims.Remove(item);
}
foreach(string role in roleListArray)
{
claimsIdentity.Claims.Add(new Claim(ClaimTypes.Role, role));
}
HttpContext.Current.User = claimsPrincipal;
}
But I am not sure if that's the right way.
Is there anyone who has done something like this??
Update 4 (01/26/2012): Found that I can use Custom ClaimsAuthencationManager(Step 4) to transform my claims.
I moved the code in AuthenticateRequest method in Global.asax to Authenticate method in ClaimsAuthenticationManager class.
I doubt it can get any better than this. I will post my solution as answer. But still if anyone has any other better solution feel free to comment.
You could use a custom ClaimsAuthencationManager, however, it will be called on every request. My recommendation would be to use WSFederationAuthenticationModule.SecurityTokenValidated. Use the ClaimsPrincipal property of SecurityTokenValidatedEventArgs class and add the roles using your provider. Also, instead of hard coding the role claim type, you may wish to consider using ClaimsIdentity.RoleClaimType.
The looked up roles will be saved in the encrypted cookie (assuming you are using the default).
The best solution would be to have an IdP (your current STS) and an RP-STS (or Federation Provider). As you say, if in the future you rely on more than one IdP (e.g. you use Live or Google, etc), it is very unlikely that they will provide the claims you need.
The purpose of the RP-STS is precisely to normalize the claimset to whatever your app requires, without polluting your app with identity concerns.
It would look like this:
An RP-STS is especially useful when you have:
Many IdP (yours and external ones)
Many Aplications
Claims transformations that can apply to many RPs. This, the RP-STS being an "authority" on the knowledge of userX being in role Y. And that knowledge not being exclusive of one app.
Protocol transition functions
The transformation (T) would add/remove claims as needed by each app, independently of the IdP.
The reason your app works when you add a "role" claim, but not with Roles.IsUserInRole, is because in general apps check User.IsInRole, which is resolved against the claimset in the principal, and is completed disconnected from Roles provider. This is arguably, a problem in the way Roles provider is designed.
The drawback of an RP-STS is the extra component you need to manage. There are however, rather simpler options today: ACS (Access Control Service) is one. If you are building a custom STS, you could do any of this of course.
The proper place to transform claims in the RP itself is by writing a Custom ClaimsAuthenticationManager (already identitfied by you). At least, that's the "official" extensibility point for doing it. Other soutions might work too though.
I have my own database (SQL Server 2008 RQ) and i have a table name "Users"
I wanna use ASP.NET MemberShip with my table (Users) how can i do this ?
I don't want to use normal DataBase, i wanna change tables and fields because i want to personalize it.
I don't want to use Profile, i want to change tables and fields, i know i can create my own authentication system but i want to use Login Control from ToolBox and other Controls.
Please give me suggestions.
This is fairly easy. You just have to create your own class inheriting from MembershipProvider and register it as a default provider. One of its methods in particular is designed to validate users' credentials. The builtin Login will use your own code, as long as you register your provider.
public class MyCustomMembershipProvider : MembershipProvider {
// a lot of methods required by the abstract class
public bool ValidateUser( string UserName, string Password ) {
// use your own database to validate user credentials
}
}
and then
<system.web>
<membership defaultProvider="MyCustomMembershipProvider">
<providers>
<add name="MyCustomMembershipProvider" type="MyCustomMembershipProvider" />
</providers>
</membership>
In case of any doubts, follow the example:
http://msdn.microsoft.com/en-us/library/44w5aswa.aspx
You can implement a custom MembershipProvider to use your own database schema.
Create your own custom membership provider; see this:
http://msdn.microsoft.com/en-us/library/aa479048.aspx
http://www.codeproject.com/Articles/165159/Custom-Membership-Providers
HTH.