Restrict same user to login from other PC - asp.net

I am learning about ASP.NET Identity and I want to restrict same user to login on different PC (by IP address ), I can do it by Session but with ASP.NET Identity I need a suggestion. Thanks

You can get an IP Address of the user by Request.UserHostAddress. If you are on a localhost server then the result will be ::1 because that is the IPv6 result of a localhost.
You can save the IP Address to the user by adding user data to the IdentityModel class. e.g.
public class ApplicationUser : IdentityUser
{
public string IP { get; set; }
}
Then you could save the IP address to the users profile by requesting their IP on sign up. You can do this by changing the data that is saved to the DB on sign up. Do this in the Account controller Register method.
var user = new ApplicationUser { UserName = model.Email, Email = model.Email,
IP = Request.UserHostAddress };
Once that's done, you can change the login method to something like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
var user = await UserManager.FindByNameAsync(model.Email);
var IP = Request.UserHostAddress;
if (IP != user.IP)
{
result = SignInStatus.Failure;
ModelState.AddModelError("", "Log in with your original computer");
return View(model);
}
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
It should be done. Don't forget to run update-database in the package-manager console.

Related

External login callback signs incorrect user

I have a strange problem with ExternalLoginCallback method. I am logging some information in the log and what is interesting Email is correct, but then userId is incorrect and it belongs to another user who was logged previously.
I.e. some UserA is logged into the system and now UserB wants to log into the system in another window. I am expecting that in new window UserB will be logged in and overwrite cookies, so if I refresh first window it will show UserB, but somehow that is not happening and in second window it shows UserA.
Here is the code:
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
EventLogManager.LogWarning(loginInfo.Email);
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: true);
switch (result)
{
case SignInStatus.Success:
{
var userId = SignInManager.AuthenticationManager.AuthenticationResponseGrant.Identity.GetUserId();
EventLogManager.LogWarning(userId);
...
EDIT
I think I should add more clarifications. There is some action which is being called from third party - Shopify. It looks like:
public ActionResult Callback(string code, string hmac, string shop, string state, string timestamp)
{
//this resolved the issue
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
Session["Workaround"] = 0;
return new ChallengeResult("Shopify", Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = "/Product/Products" }), null, shop.Replace(".myshopify.com", ""));
}

asp.net mvc multitenant database per tenant

I'm building an app for our company which needs to have separate database per client. App is for the usage of other multiple companies, so the app needs to identify the company name when the user logs in and make the users operate only within their company db.
I have it all set, but the problem is that the app is not able to handle 2 different databases simultaneously. When users from two different companies log in, the first users db gets changed to the db of the second user who is logged in! This is of course unacceptable. How can I make the app to use 2 dbs simultaneously?
I have one database which collects all app users and their company names and separate databases for each company. I also have a standard asp
below are my codes:
Login class and database initializer in account controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
bool ifDemo = false;
string demoPrefix = "demo.";
if (model.Email.StartsWith(demoPrefix))
{
ifDemo = true;
model.Email = model.Email.Substring(demoPrefix.Length);
}
SetDatabaseInitializerAndInitializeIt(ifDemo, model.Email);
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
returnUrl = CheckFirstLogin(model.Email, returnUrl);
await OnSignInSuccess(model);
//FormsAuthentication.SetAuthCookie(model.Email, false);
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
public static void SetDatabaseInitializerAndInitializeIt(bool demoDB, string login)
{
Database.SetInitializer(new ApplicationUsersSeedData());
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Facility.Data.FacilityEntities,
Facility.Migrations.Configuration>());
// Check current users company name and set the right database.
// To use demo version of database add "demo." prefix to the login email e.g: demo.testspamu79#gmail.com
using (var domain = new Facility.Models.UserCommonDBContext())
{
domain.Database.Initialize(true);
}
UserCommonDBContext context = new Facility.Models.UserCommonDBContext();
var companyName = context.CommonUser.Where(x => x.CommonUserEmail == login).FirstOrDefault().CommonUserCompanyName;
if (demoDB)
companyName = companyName + "Demo";
using (var domain = new Facility.Data.FacilityEntities(companyName))
{
domain.Database.Initialize(true);
}
}
Dbcontext:
public partial class FacilityEntities : DbContext
{
public static string DbName;
public FacilityEntities() : base(string.IsNullOrWhiteSpace(DbName) ? "Demo" : DbName)
{
}
public FacilityEntities(string dbName) : base(dbName)
{
DbName = dbName;
}
as Tielson T. said in the comments, I got rid of static and stored db name in session, and now it works!

login to .net core app with different email and username

I was building dot net core web app but identity system does not allow me to login.I figured that if my username and email address in database would not be the same it wont logged in.Anyone knows what is going on??
I'm not sure if I understood your question correctly, but the following login method on an account controller allows logins with either the username or the password:
[AllowAnonymous]
[HttpPost("login")]
public async Task<IActionResult> LoginAsync([FromBody]LoginPost model)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
// The user is identified either by Email or by Username
var user = await _userManager.FindByEmailAsync(model.Identifier)
?? await _userManager.FindByNameAsync(model.Identifier);
if (user == null)
{
return Unauthorized();
}
var signInResult = await _signInManager.PasswordSignInAsync(user, model.Password, true, false);
if (signInResult.Succeeded)
{
return NoContent();
}
return Unauthorized();
}
Please note the line where the user is looked up in the backing store: FindByEmail() ?? FindByUsername. This allows you to login with either username/password or email/password.

Invalid token in ConfirmEmail due to changed Securitystamp

I've been banging my head against a wall for some time now about this:
I have an ASP.NET MVC 5.2.3 web application with ASP.NET Identity 2.2.1. I want to force users to
validate their email-address and
validate their mobile phone number.
So when a user registers for the application an emailVerification token is generated and sent to the user.
After that the user is redirected to the VerifyPhoneNumber endpoint in the Manage controller. SMS-code is generated and gets send to the user. User is promted to enter the SMS-code. Code is verified.
BUT if then the user receives the email with the email-verification-code and click the link the token cannot no longer be verified (Invalid Token).
As far as I understand, this happens because calling UserManager.ChangePhoneNumberAsync changes the user's SecurityStamp. Email-verification works well if phone verification is not active. To be more specific, when ChangePhoneNumberAsync is not called.
Any ideas on how to prevent the SecurityStamp from changing or allow both verifications on inital registration are greatly appreciated.
Ben
VerifyPhoneNumber
public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var userId = User.Identity.GetUserId();
var result = await UserManager.ChangePhoneNumberAsync(userId, model.PhoneNumber, model.Code);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(userId);
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
return RedirectToAction("Index", "Home");
}
else
{
return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "Could not verify phone number.");
return View(model);
}
ConfirmEmail
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
code = HttpUtility.UrlDecode(code);
var result = await UserManager.ConfirmEmailAsync(userId, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}

What set's the User.Identity.Name and User.Identity.IsAuthenticated?

I want to know what set's the user identity name and change isAuthenticatedto true.
Why is User.Identity.Name an empty string and User.Identity.IsAuthenticated false after SignInManager.PasswordSignInAsync has returned Success.
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var userIdentityNameTest = User.Identity.Name; // Empty string
var result = await SignInManager.PasswordSignInAsync(
model.Email, model.Password,
model.RememberMe, shouldLockout: false);
// result is "Success"
userIdentityNameTest = User.Identity.Name;
// userIdentityNameTest is still an empty string?
// User.Identity.IsAuthenticated is still false?
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl,
RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
It seems that SignInManager.PasswordSignInAsync only validates entered data and run AuthenticationManager.SignIn if you are not using TwoFactorAuthentication. AuthenticationManager.SignIn in this case only set authentication cookie to response.
So, User.Identity is available in subsequent requests to your application. To get ApplicationUser by Name you can use ApplicationUserManager as follows:
UserManager.FindByNameAsync(model.Name)

Resources