wrong user/password didn't checked at AD - asp.net-mvc

I have created an MVC 5 Application with Windows Authentication,
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
I have below code to get user's Display name along with I also want to do validation,
protected void Session_Start(object sender, EventArgs e)
{
if (Context.User != null)
{
MapUserADDetails(Context.User);
}
}
private void MapUserADDetails(IPrincipal user)
{
using (HostingEnvironment.Impersonate())
using (var domain = new PrincipalContext(ContextType.Domain, "test.com"))
using (var usr = UserPrincipal.FindByIdentity(domain, user.Identity.Name))
{
if (usr == null)
{
return;
}
Session.Add("UserDisplayName", usr.DisplayName);
}
}
Now I am hosted this app to IIS with only windows authentication enabled. When I am browsing it, it's prompt for userName and Password,
Question,
Even I am entering wrong username/password or even doesn't fill anything, it's able to fetch Display Name.
How to restrict this? User/Pass must be validate against the AD. Please suggest. Thanks!

It sounds as IIS configuration issue and not the code.
To troubleshoot:
check if IE behaves differently
make sure that IIS has only Windows authentication enabled and not e.g. anonymous (see Receiving login prompt using integrated windows authentication)
make sure that the page has no other resources (e.g. images) used from other location that requires authentication (maybe that prompt is not for the page but for resources embedded into it)
check browser settings (e.g. in IE that site might need to be added into Intranet Zone, or "Automatically logon with current username and password" is not enabled)

You're not actually validating any username/password combination. UserPrincipal.FindByIdentity only checks if the user is found in AD.
To validate user credentials, you would need to check:
using (var domain = new PrincipalContext(ContextType.Domain, "test.com"))
{
bool authenticated = domain.ValidateCredentials(user.Identity.Name, password);
if (!authenticated)
{
// Do stuff
}
}
You can check MSDN for more info.

Related

Hangfire package in MVC on local IIS server responds with "The website declined to show this webpage" [duplicate]

I am running HangFire within my MVC web app but whenever I try to navigate to http://MyApp/hangfire, it redirects me to my app's login page as though I am not logged in.
I have not explicitly configured any requirements for authorization...e.g. I had the below in the web.config, but then took it out in attempts to get this to work.
<location path="hangfire">
<system.web>
<authorization>
<allow roles="Administrator" />
<deny users="*" />
</authorization>
</system.web>
In theory, this is what I'd want, and when I log into my main web application, I will be logged in with an Administrator role so this rule should work.
But whether I have that configured in the web.config or not, whenever I try to navigate to http://MyApp/hangfire, it redirects me to my apps login page as configured in the web.config:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="960" />
</authentication>
It does NOT do this on my local machine, just when I publish to my host. Does HangFire not recognize the authentication cookie that my main app provides when I login? I thought in general, the hangfire app doesn't require authentication, so what other configuration could be thinking that it does?
UPDATE 1:
I added the authorization filters per the hangfire docs, but the same thing happens. Here is my code in Startup.cs:
using Hangfire;
using Hangfire.Logging;
using Hangfire.Dashboard;
using Hangfire.SqlServer;
using Microsoft.Owin;
using OTIS.Web.AppCode;
using OTISScheduler.AppServ;
using Owin;
using System.Web.Security;
[assembly: OwinStartup(typeof(OTIS.Web.App_Start.Startup))]
namespace OTIS.Web.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app) {
app.UseHangfire(config => {
config.UseSqlServerStorage("DefaultConnection");
config.UseServer();
//Dashboard authorization
config.UseAuthorizationFilters(new AuthorizationFilter
{
Users = "USERA", // allow only specified users (comma delimited list)
Roles = "Account Administrator, Administrator" // allow only specified roles(comma delimited list)
});
});
LogProvider.SetCurrentLogProvider(new StubLogProviderForHangfire());
GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 0 });
var scheduleTasksInitializer = new ScheduleTasksInitializer();
scheduleTasksInitializer.ScheduleTasks();
}
}
}
UPDATE 2:
Per the more detailed instructions showing basic authentication, I also tried this...still no luck..redirects me to my app's login page.
config.UseAuthorizationFilters(
new BasicAuthAuthorizationFilter(
new BasicAuthAuthorizationFilterOptions
{
// Require secure connection for dashboard
RequireSsl = false,
SslRedirect = false,
// Case sensitive login checking
LoginCaseSensitive = true,
// Users
Users = new[]
{
new BasicAuthAuthorizationUser
{
Login = "MyLogin",
// Password as plain text
PasswordClear = "MyPwd"
}
}
}));
With the newer versions you should use IDashboardAuthorizationFilter. With the using statements, it will look like this:
using System.Web;
using Hangfire.Annotations;
using Hangfire.Dashboard;
namespace Scheduler.Hangfire
{
public class HangFireAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize([NotNull] DashboardContext context)
{
//can add some more logic here...
return HttpContext.Current.User.Identity.IsAuthenticated;
//Can use this for NetCore
return context.GetHttpContext().User.Identity.IsAuthenticated;
}
}
}
then in the configuration section:
app.UseHangfireDashboard("/jobs", new DashboardOptions()
{
Authorization = new [] {new HangFireAuthorizationFilter()}
});
Finally got it working. I created my own AuthorizationFilter class (see below).
Then I passed that to the MapHangfireDashboard method in the Startup.cs Configuration method (see below that)
public class HangFireAuthorizationFilter : IAuthorizationFilter
{
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
bool boolAuthorizeCurrentUserToAccessHangFireDashboard = false;
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if(HttpContext.Current.User.IsInRole("Account Administrator"))
boolAuthorizeCurrentUserToAccessHangFireDashboard = true;
}
return boolAuthorizeCurrentUserToAccessHangFireDashboard;
}
}
To map hangfire to a custom url and specify the AuthorizationFilter to use:
public void Configuration(IAppBuilder app) {
//Get from web.config to determine to fire up hangfire scheduler or not
app.UseHangfire(config => {
config.UseSqlServerStorage("DefaultConnection");
config.UseServer();
});
//map hangfire to a url and specify the authorization filter to use to allow access
app.MapHangfireDashboard("/Admin/jobs", new[] { new HangFireAuthorizationFilter() });
}
As designed I believe.
See the docs for the dashboard.
By default Hangfire allows access to Dashboard pages only for local requests.
Strangely enough I was dealing with this the other day and one thing to be aware of is that if you are using Autofac dependency injection then you need to make sure you configure items in the correct order. Specifically Hangfire after other authentication but also, in my case, MembershipReboot before the other OAuth stuff.
Took quite a bit of trial and error.

Why is Hangfire requiring authentication to view dashboard

I am running HangFire within my MVC web app but whenever I try to navigate to http://MyApp/hangfire, it redirects me to my app's login page as though I am not logged in.
I have not explicitly configured any requirements for authorization...e.g. I had the below in the web.config, but then took it out in attempts to get this to work.
<location path="hangfire">
<system.web>
<authorization>
<allow roles="Administrator" />
<deny users="*" />
</authorization>
</system.web>
In theory, this is what I'd want, and when I log into my main web application, I will be logged in with an Administrator role so this rule should work.
But whether I have that configured in the web.config or not, whenever I try to navigate to http://MyApp/hangfire, it redirects me to my apps login page as configured in the web.config:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="960" />
</authentication>
It does NOT do this on my local machine, just when I publish to my host. Does HangFire not recognize the authentication cookie that my main app provides when I login? I thought in general, the hangfire app doesn't require authentication, so what other configuration could be thinking that it does?
UPDATE 1:
I added the authorization filters per the hangfire docs, but the same thing happens. Here is my code in Startup.cs:
using Hangfire;
using Hangfire.Logging;
using Hangfire.Dashboard;
using Hangfire.SqlServer;
using Microsoft.Owin;
using OTIS.Web.AppCode;
using OTISScheduler.AppServ;
using Owin;
using System.Web.Security;
[assembly: OwinStartup(typeof(OTIS.Web.App_Start.Startup))]
namespace OTIS.Web.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app) {
app.UseHangfire(config => {
config.UseSqlServerStorage("DefaultConnection");
config.UseServer();
//Dashboard authorization
config.UseAuthorizationFilters(new AuthorizationFilter
{
Users = "USERA", // allow only specified users (comma delimited list)
Roles = "Account Administrator, Administrator" // allow only specified roles(comma delimited list)
});
});
LogProvider.SetCurrentLogProvider(new StubLogProviderForHangfire());
GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 0 });
var scheduleTasksInitializer = new ScheduleTasksInitializer();
scheduleTasksInitializer.ScheduleTasks();
}
}
}
UPDATE 2:
Per the more detailed instructions showing basic authentication, I also tried this...still no luck..redirects me to my app's login page.
config.UseAuthorizationFilters(
new BasicAuthAuthorizationFilter(
new BasicAuthAuthorizationFilterOptions
{
// Require secure connection for dashboard
RequireSsl = false,
SslRedirect = false,
// Case sensitive login checking
LoginCaseSensitive = true,
// Users
Users = new[]
{
new BasicAuthAuthorizationUser
{
Login = "MyLogin",
// Password as plain text
PasswordClear = "MyPwd"
}
}
}));
With the newer versions you should use IDashboardAuthorizationFilter. With the using statements, it will look like this:
using System.Web;
using Hangfire.Annotations;
using Hangfire.Dashboard;
namespace Scheduler.Hangfire
{
public class HangFireAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize([NotNull] DashboardContext context)
{
//can add some more logic here...
return HttpContext.Current.User.Identity.IsAuthenticated;
//Can use this for NetCore
return context.GetHttpContext().User.Identity.IsAuthenticated;
}
}
}
then in the configuration section:
app.UseHangfireDashboard("/jobs", new DashboardOptions()
{
Authorization = new [] {new HangFireAuthorizationFilter()}
});
Finally got it working. I created my own AuthorizationFilter class (see below).
Then I passed that to the MapHangfireDashboard method in the Startup.cs Configuration method (see below that)
public class HangFireAuthorizationFilter : IAuthorizationFilter
{
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
bool boolAuthorizeCurrentUserToAccessHangFireDashboard = false;
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if(HttpContext.Current.User.IsInRole("Account Administrator"))
boolAuthorizeCurrentUserToAccessHangFireDashboard = true;
}
return boolAuthorizeCurrentUserToAccessHangFireDashboard;
}
}
To map hangfire to a custom url and specify the AuthorizationFilter to use:
public void Configuration(IAppBuilder app) {
//Get from web.config to determine to fire up hangfire scheduler or not
app.UseHangfire(config => {
config.UseSqlServerStorage("DefaultConnection");
config.UseServer();
});
//map hangfire to a url and specify the authorization filter to use to allow access
app.MapHangfireDashboard("/Admin/jobs", new[] { new HangFireAuthorizationFilter() });
}
As designed I believe.
See the docs for the dashboard.
By default Hangfire allows access to Dashboard pages only for local requests.
Strangely enough I was dealing with this the other day and one thing to be aware of is that if you are using Autofac dependency injection then you need to make sure you configure items in the correct order. Specifically Hangfire after other authentication but also, in my case, MembershipReboot before the other OAuth stuff.
Took quite a bit of trial and error.

Login password encryption with active directory

My MVC5 application usage Active Directory and I am unable to use default .Net provided password encryption as AD doesn't support it.
My Controller is:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(Account user)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(user.UserName, user.Password))
{
var principal = user.GetUserPrincipal(user.UserName, user.Password, user.DomainName);
if (principal != null)
{
FormsAuthentication.SetAuthCookie(user.UserName, user.RememberMe);
var returnUrl = GetRedirectFromLoginUrl();
if (Url.IsLocalUrl(returnUrl))
return Redirect(returnUrl);
else
return RedirectToAction("Index", "Home");
}
else
ModelState.AddModelError("", "User Principal not created.");
}
else
{
ModelState.AddModelError("", "Login data is incorrect!");
}
}
else
ModelState.AddModelError("", "Login data is incorrect!");
return View("Login", user);
}
The login works fine however I have a security issue. I can see my username, password, domain, etc. in clear text when I capture the data using IE9 developer tools (screen below):
__RequestVerificationToken=S-DKCSoudfTYsoBh4fj...&UserName=test&Password=testpassword&DomainName=domainName
Web.Config has this code:
<membership defaultProvider="ADMembership">
<providers>
<clear/>
<add name="ADMembership" type="System.Web.Security.ActiveDirectoryMembershipProvider" connectionStringName="ADConn" attributeMapUsername="sAMAccountName" />
</providers>
</membership>
Please help how to encrypt or hide the password.
Looks like this is the default behaviour in IE developer tools. I tried to login in Google, Microsoft sites and it also shows the password in plain text.
I dont know if this is security issue in IE or not but this is how its behaving.
Steps to view your passwrod:
Open Gmail or Live.com in IE9 (I tried in IE9)
F12 (open Developer Tools) -> Go to Network tab
Click Start Capturing button
Enter username/password (you can enter anything to test)
click Stop Capturing and then go to detailed view
in grid click on POST row and Go to "Request Body" tab
see the code with password as plain text at the last of _RequestVerificationToken......
If anyone finds any better solution, please let me know.
Has this question been answered? I just created a quick, MVC4 web app using VS2012. Set the project properties SSL Enabled to true and launched the project (set the solution root to the assigned ssl:port). And yep, you CAN SEE the password (and user ID) in plain text. So nothing to do with AD - it's either intended or a bug.
NOW FOR THE INTERESTING AND SCARY - and StackOverflow, PLEASE READ.
When I do the same F12 view logging into THIS SITE, I also see my user name and password IN PLAIN TEXT!
So either this is a "feature" of IE or potentially a bug in VS and a concern for all of us in StackOverflow.
Please respond ASAP. And BTW, I tried the same test with my bank log in and nope, you see nothing or encrypted text.

Use synced Active Directory in Azure to validate users including groups?

I am porting an application to azure and in that app we use Active Directory to authenticate users like the following:
var user = model.UserName.Split('\\');
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, user[0]))
{
if (pc.ValidateCredentials(user[1], model.Password, ContextOptions.Negotiate))
{
using (var adUser = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, user[1]))
{
if (!MembershipService.ValidateUser(model.UserName, model.Password))
{
using (var userDb = new UsersDbContext())
{
if (userDb.aspnet_Users.Count(u => u.UserName.ToLower().Contains(model.UserName)) <= 0)
MembershipService.CreateUser(model.UserName, model.Password, adUser.EmailAddress);
else
{
var msUser = Membership.GetUser(model.UserName);
msUser.ChangePassword(msUser.ResetPassword(), model.Password);
}
}
}
FormsService.SignIn(model.UserName, model.RememberMe);
foreach (var role in Roles.GetAllRoles())
{
using (var group = GroupPrincipal.FindByIdentity(pc, role))
{
if (group != null)
{
if (adUser.IsMemberOf(group))
{
if (!Roles.IsUserInRole(model.UserName, role))
Roles.AddUserToRole(model.UserName, role);
}
else
{
if (Roles.IsUserInRole(model.UserName, role))
Roles.RemoveUserFromRole(model.UserName, role);
}
}
}
}
}
}
}
This works fine on our web-server which is connected to our domain server.
Now I set up an Windows Azure Active Directory and configured it to be synced with our On-Premise AD which also works.
But I am now struggeling on finding a way to connect my PrincipalContext to the WAAD.
Is this even possible and how? If not, what is the alternative?
I only found examples using Single-Sign-On which does this redirection to the MS login page we do NOT want to use, because we have a mixed authentication and depending on the entered user name it either uses the ASP.NET Membership or pulls the user and groups from AD (and actually creates an ASP.NET Membership user as seen above).
No.
You can't really use PrincipalContext with WAAD. Have to explicitly state here that you cannot currently (Jan. 2014) do direct user authentication against WAAD. You will need to rewrite some parts of your application to be compatible:
Authentication happens only on the WAAD side, your code cannot do user+password validation. This also happens on WAAD provided login page. You have limited control on how this page looks like and can customize it via Premium features of WAAD.
You can create users and reset user password using the WAAD Graph API.
Explore the Graph API for additional operations you might need (i.e. ask for user's group membership, direct reports, etc.)
You will have to switch from Windows Authentication to Federated Authentication. Depending on what VS version you are using this might be easy or tough. For VS 2012 there is Identity and Access Tool extension. While in 2013 authentication can only be configured when you create the project and cannot be altered afterwards. But you can copy configuration changes from other project over. You need changes in web.config file along with what is initialized in global.asax. Check it here - although about VS 2013 RC, the process is same in RTM.

ASP.net Forms authentication not working on iis

i working on windowsbased authentication my config looks like this
web.config
authorization
allow roles="Administrator"
allow roles="SuperUser" />
deny users="*"
authorization
in global.asax.cs
void Application_AuthenticateRequest(object sender, EventArgs e)
{
string message = string.Empty;
if (HttpContext.Current.User.Identity.IsAuthenticated)
message = HttpContext.Current.User.Identity.Name + " login successfully !";
else
message = HttpContext.Current.Request.UserHostAddress + " login failure !";
}
this works perfect in cassini. i am able to login to the application.
when i host it in IIS 7 with windows Authentication enabled.
i am getting Object reference not set to an instance of an object.
this is because for some reason HttpContext.Current.User is null
for some reason the user is not set in the httpcontext.
please help me to solve this
thanks in advance.
Managed pipeline in iis apppool is set to Integrated.
with this one turned on the httpcontext is not set on Application_AuthenticateRequest event.
this is little bit premature to access the httpcontext.
so i moved the logic to the appropriate place inside my landing controller method and it worked.
Thanks.

Resources