Session Variables Lost after ReDirect - asp.net-mvc-4

I have an MVC site that is using membership providers for authenticating against multiple domains.
I am using SESSION to store the displayName of my user that I then display in my _Layout.cshtml
This all works perfectly on my localhost, but when I deploy my app to VM/Server the SESSION is null and i get Object not set to an instance of an object and the error points to the line in my layout page where i display the full username. This happens after session timeout and user requests a new page and are required to login again.
Here is my Login method:
if (!ModelState.IsValid)
{
return View(model);
}
Session["UserName"] = "Unknown User";
Logger.Instance.Info("Setting username to unknown user");
var msg = string.Empty;
var user = _userSvc.GetLoginUserData(model.Username, out msg);
if (!string.IsNullOrEmpty(msg))
{
Logger.Instance.Info("getting user info but error returned: "+msg);
ModelState.AddModelError("", msg);
return View(model);
}
Logger.Instance.Info("User was found "+user.EMAIL);
foreach (MembershipProvider provider in Membership.Providers)
{
var username = user.LEGACY_USERNAME;
if (provider.Name == "ADENTMembershipProvider") username = user.ENT_USERNAME;
try
{
Logger.Instance.Info("Authenticating user "+username+" to "+provider.Name);
if (provider.ValidateUser(username, model.Password))
{
Session["UserName"] = user.FIRST_NAME + ' ' + user.LAST_NAME;
Logger.Instance.Info("User authenticated " + Session["UserName"]);
var authTicket = new FormsAuthenticationTicket(
1, // version
user.ID.ToString(), // user name
DateTime.Now, // created
DateTime.Now.AddMinutes(2), // expires
false, // persistent?
user.ROLE // can be used to store roles
);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
Response.Cookies.Add(authCookie);
if (returnUrl != null && returnUrl != "/")
{
Logger.Instance.Info("Redirecting user to " + returnUrl);
Response.Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
}
catch (Exception ex)
{
Logger.Instance.Error("Error authenticating user " + username + " to " + provider.Name, ex);
}
}
SynLogger.Instance.Info("User could not be authenticated " );
// User was not authenticated in any of the AD
ModelState.AddModelError("", "Invalid login attempt. The username or password provided is incorrect.");
return View(model);
Here is the line in my layout that is causing the issue when session times out and user re-logs in:
Hello #Html.Raw(Session["UserName"].ToString())
Not sure why it works on my development machine but not when I deploy to server.
Is there another way I can store the username other than SESSION so I can access it my layout page?

Related

I can't reset Active Directory user password by the admin using C# .NET Core 6

I'm Trying to reset active directory user password by .NET core web API but always return below exception, even if I put very complex password
System.DirectoryServices.AccountManagement.PasswordException:
'The password does not meet the password policy requirements.
Check the minimum password length, password complexity
and password history requirements. (0x800708C5)'
I tried both ways (DirectoryEntry and the new one) but I get the same exception.
Here is my code, but I think
public bool ResetPassword(string oldPassword, string newPassword, string userNameI)
{
/* // set up domain context
PrincipalContext context = new PrincipalContext(ContextType.Domain, LDAP_PATH, userName, password);
if (context != null)
{
// find the user you want to delete
UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, userNameI);
if (user != null)
{
user.Enabled = true;
user.ChangePassword(oldPassword,newPassword);
user.ExpirePasswordNow();
user.Save();
return true;
}
}*/
/*
var entry = new DirectoryEntry
{
Path = "LDAP://MyIP",
Username = userName,
Password = password
};
using (var searcher = new DirectorySearcher(entry))
{
searcher.Filter = "(SAMAccountName=" + userNameI + ")";
var result = searcher.FindOne();
var user = result.GetDirectoryEntry();
user.Invoke("ChangePassword", new object[] { oldPassword.Trim(), newPassword.Trim() });
user.CommitChanges();
return true;
}
*/
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "LDAPIP", userName, password))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, userNameI))
{
if (user != null)
{
user.ChangePassword(oldPassword, newPassword);
user.Save();
return true;
}
else
{
throw new Exception(string.Format("Username not found: {0}", userNameI));
}
}
return false;
}
}
The below code is working fine, the Old password is not needed in resetting password and account owner user name is needed :
public bool ResetPassword(string newPassword, string accountOwneruserName /*user name for the user that you want to change his password*/)
{
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "17x.xx.xx.x" /*Active Directory server Ip*/, adminUserName, adminPassword ))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, accountOwneruserName))
{
if (user != null)
{
user.SetPassword(newPassword.Trim());
user.Save();
return true;
}
else
{
throw new Exception(string.Format("Username not found: {0}", accountOwneruserName));
}
}
return false;
}
}

Problems Creating Auth User in ASP.NET Core 5

I have a VS Project I built, basically just and ASP.NET Core 5 Web Site with local Auth enabled. I can register local users, then login to them no problem. However, when I try to create a user programmatically, with the code below,I know the user is created because I can view it in SQL, however when I try and login, I get a "login access no allowed" error.
Am I somehow not creating the user correctly?
How can I debug this to see what is not validating?
public static async Task SeedDefaultUserAsync(UserManager<ApplicationUser> userManager)
{
string testUser = "user999";
var user = await userManager.FindByNameAsync(testUser);
if (user == null)
{
user = new ApplicationUser()
{
Email = testUser + "#gmail.com",
UserName = testUser,
EmailConfirmed = true,
LockoutEnabled = false,
};
await userManager.CreateAsync(user, "Abc123#");
Console.WriteLine("user: " + testUser + " available with password Abc123#.");
}
if (user == null)
{
throw new Exception("The password is probably not strong enough!");
}
}
I've even tried sending myself a password reset link, reset the password and I still get the login error.
As far as I know, when you login in asp.net core identity it will called the CanSignInAsync method to check the user could signin or not.
If user doesn't pass , it will throw the "login access no allowed" error.
More details, you could refer to below source codes:
protected virtual async Task<SignInResult> PreSignInCheck(TUser user)
{
if (!await CanSignInAsync(user))
{
return SignInResult.NotAllowed;
}
if (await IsLockedOut(user))
{
return await LockedOut(user);
}
return null;
}
public virtual async Task<bool> CanSignInAsync(TUser user)
{
if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user)))
{
Logger.LogWarning(0, "User cannot sign in without a confirmed email.");
return false;
}
if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user)))
{
Logger.LogWarning(1, "User cannot sign in without a confirmed phone number.");
return false;
}
if (Options.SignIn.RequireConfirmedAccount && !(await _confirmation.IsConfirmedAsync(UserManager, user)))
{
Logger.LogWarning(4, "User cannot sign in without a confirmed account.");
return false;
}
return true;
}
So I suggest you could check your startup.cs to make sure you have set the phone or RequireConfirmedAccount to false like below, if you have enabled other required information you should set it in your application.
Besides, by default the username is also a Email format, please modify your codes to set username to Email also. UserName = testUser + "#gmail.com",
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<IdentityUser>(options =>
{ options.SignIn.RequireConfirmedAccount = false;
options.SignIn.RequireConfirmedEmail = true;
options.SignIn.RequireConfirmedPhoneNumber = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
}

MVC [Authorize] in homecontroller not working for every user

So I am using LDAP to authenticate my users, and this works fine for most all of my users, but for some of them it does not. I know for a fact I have two users that it is not working for. The code pulls down all the information about the person, creates a formsAuthenticationticket, but when it gets to [authorize] it just bounces them back to the login page. The question is why?
Login controller:
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
string logon_user = model.UserName.ToString();
string logon_password = model.Password.ToString();
ConnHelper connhelper = new ConnHelper();
string encryptedTicket = null;
String adPath = "#####"; //Path to the 2003 LDAP directory server
ADAuthorize adAuth = new ADAuthorize(adPath);
FormsAuthenticationTicket authTicket = null;
try
{
if (true == adAuth.IsAuthenticated("#####", logon_user, logon_password))
{
string groups = adAuth.GetGroups();
Account acc = new Account();
acc.windows_id = logon_user;
acc.password = logon_password;
acc.gers_id = connhelper.GetGersID(acc.windows_id);
acc.region = connhelper.IsNull(connhelper.GetRegionManager(acc.gers_id));
acc.home_store_region = connhelper.IsNull(connhelper.GetHomeStoreRegion(acc.gers_id));
acc.store_group = connhelper.IsNull(connhelper.GetStoreGroup(acc.gers_id));
acc.home_store = connhelper.IsNull(connhelper.GetStore(acc.gers_id));
acc.arr = connhelper.GetStores(acc.gers_id);
//acc.home_store_phone = misc.IsNull(misc.GetHomeStorePhoneNumber("hzs"), "");
acc.home_store_phone = connhelper.IsNull(connhelper.GetHomeStorePhoneNumber(acc.gers_id), "");
acc.full_name = connhelper.IsNull(connhelper.GetFullName(acc.gers_id), "");
// Onlt use the following in the core
// acc.full_name = adAuth.getuserFname("#####", logon_user, logon_password);
misc.GetStore(acc.gers_id);
//Add information to the session
Session.Add("roles", groups);
Session.Add("Account", acc);
// Create the authentication ticket
authTicket =
new FormsAuthenticationTicket(1, // version
acc.windows_id,
DateTime.Now,
DateTime.Now.AddMinutes(500),
false, groups);
// Now encrypt the ticket.
encryptedTicket = FormsAuthentication.Encrypt(authTicket);
// Create a cookie and add the encrypted ticket to the cookie as data.
HttpCookie authCookie =
new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
// Add the cookie to the outgoing cookies collection.
Response.Cookies.Add(authCookie);
if (FormsAuthentication.GetRedirectUrl(acc.windows_id, false).EndsWith("Logout.aspx"))
{
return RedirectToAction("Login", "Account");
}
//
// Validate code this does the redirect to where you want the logged in person to go to.
//
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "Authentication failed, check username and password.");
return View(model);
}
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error authenticating. " + ex.Message + ex.StackTrace);
return View(model);
}
// return View(model);
}
Adauth is authenticated ( this returns true even for the people who get bounced)
public bool IsAuthenticated(string domain, string username, string pwd)
{
cred(username, pwd);
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{
// Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("SAMAccountName");
//search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
// Update the new path to the user in the directory
_path = result.Path;
_filterAttribute = (String)result.Properties["SAMAccountName"][0];
//_filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return true;
}
Then the home index controller
[HttpGet]
[Authorize]
public ActionResult Index()
{
//grab all events and pass to view
//
int count = D.getEventRows();
if (count != 0)
{
Event[] events = new Event[count];
events = D.getEvents(count);
ViewBag.host = globals.hosts();
ViewBag.events = events;
DateTime curr = DateTime.Now;
ViewBag.curr = curr;
return View(events);
}
return View();
}
web config:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="28800" />
</authentication>
<authorization>
<allow users="*" />
</authorization>

Invalid token in reset password of Asp.Net web api

I'm working on a project using Asp.NET web api, and my authentication system is based on identity 2.0. When user sends the ResetPassword form, he gets "Invalid token"
this is my forgotpassword method
public async Task<HttpResponseMessage> ForgotPassword(ForgotPasswordViewModel model)
{
if (!ModelState.IsValid)
{
HttpError error = new HttpError(ModelState, false);
error.Message = Resource.No_Item_Found_Message;
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, error);
}
var user = await UserManager.FindByEmailAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
// Don't reveal that the user does not exist or is not confirmed
HttpError error = new HttpError();
error.Message = Resource.Process_Failed;
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, error);
}
var provider = new DpapiDataProtectionProvider("ECommerceWebApp");
UserManager.UserTokenProvider = new DataProtectorTokenProvider<ECommerceUser, string>(provider.Create("UserToken"));
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
code = HttpUtility.UrlEncode(code);
try
{
// var callbackUrl = new Uri(Url.Link("ResetPasswordRoute", new { userId = user.Id, code = code, newPassword = model.Password }));
var callbackUrl = Url.Link("Default", new { Controller = "Account", action = "ResetPassword", userId = user.Id, code = code });
await UserManager.SendEmailAsync(user.Id, "تغییر رمز عبور در IRI1", "<div style='font-family:tahoma;direction:rtl;text-align:right;font-size:12px;'>" + "<h3>اولین و بزرگترین مرکز دادوستد بدون واسطه در ایران و کشورهای همسایه</h3>لطفاً با کلیک بر روی گزینۀ تغییر رمز به صفحۀ مربوطه بروید : <br/><br/>تغییر رمز عبور <br/><br/><br/><a href='iri1.com'>Iri1 Web Sites</a>" + "</div>");
}
catch (Exception ex)
{
HttpError error = new HttpError();
error.Message = ex.Message;
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, error);
}
return Request.CreateResponse(Resource.Reset_Password_Message_Client);
}
and this is my ResetPassword method
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByEmailAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var code = HttpUtility.UrlDecode(model.Code);
var result = await UserManager.ResetPasswordAsync(user.Id, code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
I still get invalid token error
I've just begun learning Identity Authentication and have no way to run samples from the Internet(I've got no visual studio). But I've noticed this method from GidHub samples, which seems to me to be wrong:
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPassword(string code = null)
{
return code == null ? View("Error") : View();
}
In my humble opinion this method should do this:
return code == null ? View("Error") : View(new ResetPasswordViewModel{Code = code});
And thus the hidden field in ResetPassword view contains the Code. When the user clicks the submit button this code will be posted with the email and password to the httppost ResetPassword action where you can access the code thus: model.Code
Now I hope you've got a valid token

FBA dual authentication problem

What I have?
I have configured FBA in one of the web applications with out of the box login page having dropdown box to select the either windows or FBA login. Everything is working fine.
What I want?
I want to have a custom login page having text boxes for Username and Password and a login button which will be used for authenticating both Windows and FBA users. To distinguish between the two different logins, I want to handle OnAuthenticate event and check if the user name contains a '\' then assume it is Windows user otherwise, it is FBA user.
This is the code written in OnAuthenticate event handler:
protected void signinControl_Authenticate(object sender, AuthenticateEventArgs e)
{
string fullUserName = signinControl.UserName;
string username = null;
if (fullUserName.Contains("\\")) //Windows user
{
string domain = fullUserName.Substring(0, fullUserName.IndexOf("\\"));
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain))
{
username = fullUserName.Substring(fullUserName.IndexOf("\\") + 1);
e.Authenticated = pc.ValidateCredentials(username, signinControl.Password);
}
}
else //FBA user
{
e.Authenticated = Membership.ValidateUser(fullUserName, signinControl.Password);
}
}
What problem am I facing?
The code above works well for FBA Users. But, when I try to login with a windows user, even though the e.Authenticated is set true after validating, it is throwing this error: "Your login attempt was not successful. Please try again.".
e.Authenticated = pc.ValidateCredentials(username, signinControl.Password);
I believe that, setting e.Authenticated to true should redirect the user from login page to the requested page. Can someone please help me if I have to do anything else to get Windows user signed in?
Update-1:
I used SetAuthCookie() method to set Cookie explicitly, still the same result.
FormsAuthentication.SetAuthCookie(username, true);
you should use the methode below for forms user
SPClaimsUtility.AuthenticateFormsUser(
Context.Request.UrlReferrer,
UserName.Text,
Password.Text);
and the windows part is declared like this:
protected void lbInternalUsers_OnClick(object sender, EventArgs e)
{
try
{
if (null != SPContext.Current && null != SPContext.Current.Site)
{
SPIisSettings iisSettings = SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default];
if (null != iisSettings && iisSettings.UseWindowsClaimsAuthenticationProvider)
{
SPAuthenticationProvider provider = iisSettings.WindowsClaimsAuthenticationProvider;
Redirect(provider);
}
}
}
catch (Exception ex)
{
lblError.Text = ex.Message;
}
}
private void Redirect(SPAuthenticationProvider provider)
{
string comp = HttpContext.Current.Request.Url.GetComponents(UriComponents.Query, UriFormat.SafeUnescaped);
string url = provider.AuthenticationRedirectionUrl.ToString();
if (provider is SPWindowsAuthenticationProvider)
{
comp = EnsureUrl(comp, true);
}
SPUtility.Redirect(url, SPRedirectFlags.Default, this.Context, comp);
}
private string EnsureUrl(string url, bool urlIsQueryStringOnly)
{
if (!url.Contains("ReturnUrl="))
{
if (urlIsQueryStringOnly)
{
url = url + (string.IsNullOrEmpty(url) ? "" : "&");
}
else
{
url = url + ((url.IndexOf('?') == -1) ? "?" : "&");
}
url = url + "ReturnUrl=";
}
return url;
}
as detailed here in the reference