MVC 4 with AspNetSqlMembershipProvider - asp.net-mvc-4

I've been tasked with rewriting an existing Asp.Net Classic web app in MVC4, using the existing DB and authentication.
The old app still uses the "aspnet_" prefix to its tables. I've modified the new site's web.config to include these providers (copied straight form the old site)
<roleManager enabled="true">
<providers>
<remove name="AspNetSqlRoleProvider" />
<add connectionStringName="LocalSqlServer" applicationName="MyApp" name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<remove name="AspNetWindowsTokenRoleProvider" />
<add applicationName="IOL" name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</roleManager>
<membership defaultProvider="AspNetSqlMembershipProvider">
<providers>
<remove name="AspNetSqlMembershipProvider" />
<add connectionStringName="LocalSqlServer" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="MyApp" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<remove name="AspNetAdminMembershipProvider" />
<add connectionStringName="LocalSqlServer" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" applicationName="IOL" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" name="AspNetAdminMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</membership>
In my account controller's login action:
public ActionResult Login(LoginModel model, string returnUrl)
{
var auth = Membership.ValidateUser(model.UserName, model.Password);
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
The auth variable is true if I remove the [InitializeSimpleMembership] from the controller, if I add it back, I get an error stating "Cannot convert type 'System.Guid' to 'int'". he Websecurity.Login() method fails both with and without the class attribute.
So in a nutshell, if I remove the simple membership, the forms auth validate user works, but when I inspect the User.Identity.IsAuthenticated object, is says false.
What am I missing? I'm not even sure if I'm implementing this security model correctly, I can't find any information on it.

You cannot use the old membership providers with SimpleMembership (e.g. WebSecurity). You need to use the SimpleMembershipProvider. And you will have to migrate the data from the old table structures to the schema used by SimpleMembership. The only thing you can customize in SimpleMembership tables is the table that contains the user profile information, which you can read about here.
Migrating to MVC4 and SimpleMembership will already put you behind the curve. SimpleMembership has been depreciated for ASP.NET Identity. ASP.NET Identity requires MVC5, which you should migrate to anyway to take advantage of all of the added features as well of the general benefits of keeping your underlying components current. ASP.NET Identity is much more flexible than SimpleMembership (SimpleMembership was designed to be simple and not exactly flexible) and there is even an article that walks you through the steps of migrating SQL Membership to ASP.NET Identity.

Related

What is the Alternative to Federated Authentication in ASP.Net Core?

I have a Web API (.Net 4.6) which authenticates my users using the FederatedAuthentication (System.IdentityModel.Services), and now I am trying to port it to ASP.Net Core 2.2 Web API.
Here is my existing code to generate the FedAuth token cookie:
1. AuthController.cs
//... Create new Identity and Claims Principal.
ClaimsIdentity claimsIdentity = new ClaimsIdentity(User.Identity);
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
claimsIdentity.AddClaims(__getClaims());
claimsPrincipal.AddIdentity(claimsIdentity);
//... Create a new Session Security Token.
var token = FederatedAuthentication.SessionAuthenticationModule.CreateSessionSecurityToken(
claimsPrincipal, "MyAPP", DateTime.UtcNow, DateTime.UtcNow.AddMinutes(expirationTime), false);
//... Write a cookie.
FederatedAuthentication.SessionAuthenticationModule.
AuthenticateSessionSecurityToken(token, true);
And in the Web.config:
<configSections>
<!--WIF 4.5 sections -->
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
<modules>
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</modules>
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
Since Claims Identity and Claims Principal is used by all of my relying applications, I want to continue using the same in ASP.net core too.
So, my question here is that, what is the way to create a session security token
(cookie) with claims Identity in ASP.net core web API?
Thanks a lot!!
I believe I got what I was looking for;
Here are the two good articles I found out to start with:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-2.2
https://jonhilton.net/2017/10/11/secure-your-asp.net-core-2.0-api-part-1-issuing-a-jwt/
EDIT: The second link was updated by their author that resulted in 404 error. I found the working link and updated back here.
NOTE: The reason I have posted only links here to answer my own question is that they are long articles and has to be read in length to understand the subject.

MVC Application is looking for credentials in the wrong database

I have an aspnetservicesdb database that is used for storing user profiles. I have deployed a new asp.net mvc application. The application uses a local sqlexpress database in the appdata directory for membership/profile purposes.
I do not want the application to use the sqlexpress database.
I've removed the connection string from the web.config and AuthConfig.RegisterAuth(); from global.asax
So far, this has worked. I'm able to use User.Identity.Name in the controller succesfully. However, when I tried to use User.IsInRole in the view, I got a sql server not found error, becuase the local sqlexpress db doesn't exist.
How do I tell the application to use my existing database and stop looking for the sqlexpress db?
edit - here are the connection strings
<!--<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=removed;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|removed" providerName="System.Data.SqlClient"/>-->
<add name="ApplicationServices" connectionString="Data Source=removed;Initial Catalog=aspservicesdb;UID=removed;PWD=removed" providerName="System.Data.SqlClient" />
edit - here are other membership portions from web config
<authentication mode="Forms">
<forms loginUrl="~/login.aspx" timeout="2880" />
</authentication>
<membership userIsOnlineTimeWindow="480">
<providers>
<clear />
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="12" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
You're not telling asp.net to use the membership provider. That should look like this:
<membership defaultProvider="AspNetSqlMembershipProvider" userIsOnlineTimeWindow="480">
And, since you're getting membership some other way, that tells me you're probably using SimpleMembership, which means you need to remove the SimpleMembership code from your application (including the user data context, and other attributes)
It turns out I hadn't added these sections to the web config...
<profile>
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</profile>
<roleManager enabled="true">
<providers>
<clear />
<add connectionStringName="ApplicationServices" applicationName="/"
name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</roleManager>
The issue appears to be resolved.

How do I get WebSecurity.Login to work?

I'm trying to get the default login model for MVC4 working, but out of the box, I don't think I'm doing it right. This is the default code:
if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
When I build, run and try to log in, I get the following error:
An exception of type 'System.InvalidOperationException' occurred in WebMatrix.WebData.dll but was not handled in user code
Additional information: To call this method, the "Membership.Provider" property must be an instance of "ExtendedMembershipProvider".
I thought it was an issue with my provider resources, but I've installed Entity First Tools 6.0.2, System.web.providers, and Microsoft.aspnet.providers.core and am still getting this issue. I found some indications, it might be because I'm connecting to a SQL database, but I couldn't find a resolution.
Someone indicated I need to use System.Web.Http instead of System.Web.Mvc, but that wasn't it.
This is my connection string:
<add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Server=servername;Initial Catalog=mercury;Integrated Security=SSPI" />
This is my profile and membership info:
<profile defaultProvider="DefaultProfileProvider">
<providers>
<add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</profile>
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
I read something else indicating I needed to initialize my SimpleMembershipProvier, but I have a custom initializer built to run from application_start so I an seed my database with data:
Database.SetInitializer<UsersContext>(new DatabaseInitializer());
new UsersContext().UserProfiles.Find(1);
My data initializer has this code to initialize my database:
private void SeedMembership()
{
WebSecurity.InitializeDatabaseConnection("UsersContext", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
So, I have no idea where to look at next.
There are a few places where things could have gone wrong. For starters I'd downgrade Entity 6 to Entity 5 since EF 6 is made to support MVC which uses the Claims Identity model rather than Simple Membership API. You can go to package manager console and choose the relevant app
Uninstall-Package EntityFramework Version 6.0.2
then
Install-Package EntityFramework version 5.0.0
Hope this gets you going.
You can also wrap the Initializer in a check method
if(!Websecurity.Initialized){
///Initialize Websecurity
}

"The Role Manager feature has not been enabled", configuration.cs is in a separate project

I have an MVC4 application with the models and the migrations in a separate project, which is a class library.
I am trying to seed the database with old users using the following code inside my seed method
foreach (var subcontractor in context.Subcontractors)
{
WebSecurity.CreateUserAndAccount(subcontractor.Email, subcontractor.ObsoletePlainTextPassword);
}
I copied the following code from my main project into the app.config, but I don't think it's getting picked up.
<profile defaultProvider="DefaultProfileProvider">
<providers>
<add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</profile>
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
<roleManager defaultProvider="DefaultRoleProvider">
<providers>
<add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</roleManager>
Where I run update-database, i get The Role Manager feature has not been enabled. error.
What am I missing?
The problem was the fact that my "Data" project wasn't a StartUp project. App.config wasn't being read, and that was causing the error.
MVC4 rebuilt (or more borrowed you can say) the authentication system from WebMatrix, so make sure you're not mixing any code from the prior auth system. But if you're using the default security and authentication (which is now SimpleMembership) in there and you're trying to seed roles, you need to enable the SimpleRoleProvider in the web.config. Are you trying to seed a user or users with corresponding roles in the seed method for Entity migrations? If you're not seeding the database with roles, then it might be something else. Test this by removing any mention of seeding roles in the migrations/congiguration.cs file. So be sure any mention of SimpleRoleProvider is out of the configuration.cs file, or try the following code in the web.config.
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<clear/>
<add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<clear/>
<add name="SimpleMembershipProvider"
type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData"/>
</providers>
</membership>
The reason for this is while the SimpleRoleProviders is available as part of the MVC when the website is running, this role provider has to be explicitly configured as part of the seed method for migrations. Otherwise the seed method in migrations won't pick it up.

You must call the "WebSecurity.InitializeDatabaseConnection"

I have a problem with using [Authorize(Roles = "admin")] attribute.
[Authorize(Roles = "admin")]
public ActionResult GetAllLocations()
{
I am getting the following error
You must call the "WebSecurity.InitializeDatabaseConnection" method
before you call any other method of the "WebSecurity" class. This call
should be placed in an _AppStart.cshtml file in the root of your site.
I have built a MVC 4 application using EF 5 Code first with my own database.
A little background:
I have created a custom membership provider I have inherited from
MembershipProvider
I have look at a lot of questions in this site about this issue, but
didn't found an answer.
In some answers i saw how to disable the membership provider
like this:
<add key="enableSimpleMembership" value="false"/>
<add key="autoFormsAuthentication" value="false"/>
This is how i implemented my custom membership provider
<membership defaultProvider="ATWMembershipProvider">
<providers>
<clear/>
<add name="ATMMembershipProvider" type="AroundTheWorldWeb.Infrastructure.AuthenticationProvider.ATMMembershipProvider"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
equiresUniqueEmail="false"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="6"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="10" applicationName="myApplication" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="AspNetSqlRoleProvider">
<providers>
<remove name="AspNetSqlRoleProvider" />
<add name="AspNetSqlRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<!-- note: WebMatrix registers SimpleRoleProvider with name
'AspNetSqlRoleProvider'. I don't know why but i kept it. -->
</providers>
</roleManager>
I fixed this problem by creating the MVC 4 from template. I think there some DLL's aren't loaded when choosing Empty template. So i have created it from a template and override all the Account's methods And also implemented custom member and role provider