ASP.NET MVC5 with existing Membership database - asp.net

I have an existing ASP.NET Membership database which I have to stick to. I'm developing a new MVC5 website in VS2013 (update 4) using a new MVC website project template. I have modified web.config to ensure the old Membership type of authentication is specified. I have also modified generated [HttpPost]Login action to ensure I login against my Membership database - it logs me in correctly and generates an authentication cookie as required.
However the website still redirects me to the Login page as I'm not authenticated. The Request.IsAuthenticated does show that I'm not authenticated. What am I missing? What are my options?
EDIT:
web.config changes (only changes):
<connectionStrings>
<add name="MyCS" connectionString="Data Source=sql1;Initial Catalog=MyDB;Persist Security Info=True;User ID=mysa;Password=mypw" providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<authentication mode="Forms">
<forms timeout="20" name="seAdmin" loginUrl="~/Account/Login" />
</authentication>
<roleManager enabled="true" defaultProvider="CustomizedRoleProvider">
<providers>
<clear />
<add name="CustomizedRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="MyCS" applicationName="/seAdmin" />
</providers>
</roleManager>
<membership defaultProvider="CustomizedMembershipProvider">
<providers>
<clear />
<add name="CustomizedMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="MyCS" applicationName="/seAdmin" />
</providers>
</membership>
<machineKey validationKey="63A7C07B191BA3EF02DD4866C420DCAB81C9FFCCC617DE40ED6E2B89A2FC2BA3FA32C39D183FE0708E9279C14E58318D0C5E171C0AF802F154430679D1778485" decryptionKey="F5B9049DECB8C9A23B1D131E63D2ED5C15FF0AEB3C3E96FC" validation="SHA1" />
</system.web>
Login action:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
if (Membership.ValidateUser(model.Email, model.Password))
{
FormsAuthentication.SetAuthCookie(model.Email, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1)
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
return View(model);
}
FilterConfig adds a global authorisation attribute
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute() { Roles = "CWA" });
}
}

I have some theories as to why it doesn't work, firstly in web.config there is a <system.webServer> tag, there must be a line for removing FormsAuthentication, try commenting it out, or add one again, like so:
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
</modules>
secondly try User.Identity.IsAuthenticated to see it is false too, also are you have any role in your app?

Related

Web API 2 EnableCors not working when I post data with DHC Chrome extension

I am developing a Web API 2 project and I using EnableCors attribute like this:
Controller:
[EnableCors(origins: "http://localhost:32454, http://localhost:25234", headers: "*", methods: "POST")]
WebApiConfig:
config.EnableCors();
Web.config:
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
When I am posting data to my Web API via DHC Chrome extension, my controller is working fine. But, I set two origin. I don't want to access my Web API via DHC. Because, I didn't allow it.
What should I do?
I usually use Owin with Web Api and this should resolve:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
WebApiConfig.Register(config);
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
}

ASP.NET API Windows authentication w/o login

Intranet application that needs to use Windows AD for authorization. Anyone will have access to the site, but only members of a specific AD group will have access to certain pages and API calls. The group name is in the web.config as it will vary by environment. I want to be able to use attributes on API controllers and methods:
[MyAdminAttribute]
public class MyController : ApiController
Here is my custom attribute:
public class MyAdminAttribute: AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var user = ((ApiController)actionContext.ControllerContext.Controller).User; // THIS USER IS ALWAYS NULL, WHY?
var adminGroup = WebConfigurationManager.AppSettings["MyAdminGroup"];
var isInAdminGroup = user.IsInRole(adminGroup);
var roles = ((ClaimsIdentity)user.Identity).Claims
.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value).ToList(); // Just checking here, 0 roles found.
return isInAdminGroup;
}
}
I also need to be able to check that the user is in the role within a Razor view:
#if (User.IsInRole(WebConfigurationManager.AppSettings["MyAdminGroup"])) {
Here are the relevent web.config settings:
<authentication mode="Windows"/>
<roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider">
<providers>
<clear />
<add
name="AspNetWindowsTokenRoleProvider"
type="System.Web.Security.WindowsTokenRoleProvider"
applicationName="/" />
</providers>
</roleManager>
However, the User is always null and User.IsInRole() is always false. Why would this be occuring?

MembershipProvider change connection string in code

I am trying to implement Asp.net Forms Authentication in my site. Usually, you provide the name of your DB connection string in your web.config. However, as our database setup is a little more complicated, I was looking for a way to manually provide the MembershipProvider the connection string in code.
Thanks!
You don't have to use a connectionStringName for SqlMembershipProvider, instead you can supply the connection string directly. I.e. instead of:
<membership defaultProvider="SqlProvider" ...>
<providers>
<add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider"
connectionStringName="MyConnectionStringName"
.../>
</providers>
</membership>
You can specify the connection string directly as:
<membership defaultProvider="SqlProvider" ...>
<providers>
<add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider"
connectionString="data source=... "
.../>
</providers>
</membership>
Therefore you could also derive a custom provider from SqlMembershipProvider, and build the connection string dynamically as follows:
public class MySqlMembershipProvider : SqlMembershipProvider
{
public override void Initialize(string name, NameValueCollection config)
{
config["connectionString"] = BuildMyCustomConnectionString();
base.Initialize(name, config);
}
}
I came across this needing to do the same thing, set the connection string via code and not in the web.config, although I needed to change more than the name, I needed the actual value to be dynamically generated. If you want to change the actual connection string to be generated from code you can do the following:
web.config
...
<connectionStrings>
<add name="ConnectionPlaceHolder" connectionString="This is a place holder"/>
</connectionStrings>
...
<roleManager defaultProvider="SqlRoleProvider" enabled="true">
<providers>
<clear/>
<add name="SqlRoleProvider" type="MyClassLibraries.Web.Security.MyCustomSqlRoleProvider" connectionStringName="ConnectionPlaceHolder" applicationName="MyApplicationName"/>
</providers>
</roleManager>
Provider Class
public class MySqlRoleProvider : SqlRoleProvider
{
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
try
{
config["connectionStringName"] = "ConnectionPlaceHolder";
base.Initialize(name, config);
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, ApplicationConfiguration.RetrieveApplicationConfigurationValue(myConnectionString));
}
catch (Exception ex)
{
CurrentOperation.RaiseException(ex);
throw ex;
}
}
private string myConnectionString()
{
return "Server=MyServer;database=MyDB;uid=aspnetDBUser;pwd=myPassword"
}
}
When you call base.Initialize() the .NET class requires there be a name specified in the web.config which is why you have to put something, so I just used a placeholder since I knew I would be overriding it in the code.
Our team did this because we needed to dynamically build connection strings based on different environments and didn't want to have to worry about having multiple web.configs floating around.

Which Membership Provider implements users stored in web.config?

Having a code-blind moment.
ASP.NET 4.0.
Web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="DataViewer" loginUrl="login.aspx">
<credentials passwordFormat="Clear">
<user name="devuser" password="test" />
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
</system.web>
and a login control:
<asp:Login ID="login" runat="server" />
If I enter a username and password, and click Login, it hangs.
If I break, I can see in the call stack that login.AuthenticateUsingMembershipProvider() is in the middle of calling SqlMembershipProvider.ValidateUser(). There is no database defined or involved in this project at all, and I haven't specified that SqlMembershipProvider should be used.
So my question is, what membership provider should I use to get ASP.NET to use the usernames and passwords in the <credentials> element of web.config?
I'm amazed that considering how the framework designers went to the trouble of defining a <credentials /> element that they didn't implement any code to consume it.
I found a sort-of-working implementation of this here which I have fixed up and included below. All other members of MembershipProvider throw NotImplementedException.
using System.Configuration;
using System.Web.Configuration;
using System.Web.Security;
public class WebConfigMembershipProvider : MembershipProvider
{
private FormsAuthenticationUserCollection _users = null;
private FormsAuthPasswordFormat _passwordFormat;
public override void Initialize(string name,
System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
_passwordFormat = getPasswordFormat();
}
public override bool ValidateUser(string username, string password)
{
var user = getUsers()[username];
if (user == null) return false;
if (_passwordFormat == FormsAuthPasswordFormat.Clear)
{
if (user.Password == password)
{
return true;
}
}
else
{
if (user.Password == FormsAuthentication.HashPasswordForStoringInConfigFile(password,
_passwordFormat.ToString()))
{
return true;
}
}
return false;
}
protected FormsAuthenticationUserCollection getUsers()
{
if (_users == null)
{
AuthenticationSection section = getAuthenticationSection();
FormsAuthenticationCredentials creds = section.Forms.Credentials;
_users = section.Forms.Credentials.Users;
}
return _users;
}
protected AuthenticationSection getAuthenticationSection()
{
Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
return (AuthenticationSection)config.GetSection("system.web/authentication");
}
protected FormsAuthPasswordFormat getPasswordFormat()
{
return getAuthenticationSection().Forms.Credentials.PasswordFormat;
}
}
You are going to need to write your own provider for this. It should be relatively straightforward to take the sample ReadOnlyXmlMembershipProvider in the MSDN documentation and change it to read users and credentials from web.config, instead of an external XML file.
I'm not sure if you have tried but....
The FormsAuthentication.Authenticate is in charge to do that for you (although it is deprecated now because the recommended behavior is to use the Membership object)
From MSDN:
The Authenticate method verifies user credentials that are stored in the credentials section of the application configuration file. Alternatively, you can use ASP.NET membership to store user credentials and call the ValidateUser to verify the credentials.
You can also remove the membership providers (because even when you do not declare them on your web.config, they are inherited from the machine.config file)
<membership>
<providers>
<remove name="AspNetSqlMembershipProvider"/>
</providers>
</membership>
Try using this method. I hope it helps. http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.login.onauthenticate.aspx

Why don't trace listeners log custom handler traffic?

I have a custom handler like this,
public class Handler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("Hello World");
}
public bool IsReusable
{
get
{
return false;
}
}
}
Why does this not work?
<system.diagnostics>
<sources>
<source name="System.Web" switchValue="All" propagateActivity="true">
<listeners>
<add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\logs\Traces_Documents.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
When that is in the web.config it does nothing when I call the handler. No logging etc.
I have previously used the same trace listener on the System.ServiceModel namespace with a WCF service, and that worked.
Do you have tracing enabled?
<system.web>
<trace enabled="true" writeToDiagnosticsTrace="true" />
</system.web>
Also, what version of the framework / IIS are you using?
See the DnrTV episode w/ Steve Smith which has good information on Asp.Net Tracing.

Resources