How to pass a claim in a cookie in ASP.NET Identity? - c#

I am trying to save a Guid value in a cookie. The way I want to save it is by setting a Claim for the user and getting it back in every controller for validation purposes.
Before going on the subject I have seen this question here but it did not solve my problem!
Here is my code in login controller:
public async Task<ActionResult> Login_Post(LoginViewModel loginViewModel)
{
if (ModelState.IsValid)
{
var user = IdentityManager.UserManager.FindByName(loginViewModel.Username);
if (user != null)
{
IdentityManager.SignInManager.AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var status = SignInStatus.Failure;
if (!user.IsApproved)
{
status = SignInStatus.Failure;
}
if (!await IdentityManager.UserManager.IsEmailConfirmedAsync(user.Id))
{
status = SignInStatus.EmailNotConfirmed;
}
status = (SignInStatus)await IdentityManager.SignInManager
.PasswordSignInAsync(loginViewModel.Username, loginViewModel.Password, loginViewModel.RememberMe, true);
if (status == SignInStatus.Failure)
{
await IdentityManager.UserManager.AccessFailedAsync(user.Id);
if (await IdentityManager.UserManager.IsLockedOutAsync(user.Id))
{
status = SignInStatus.LockedOut;
}
}
switch (status)
{
case SignInStatus.Success:
var identity = await IdentityManager.UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
var permissions = IdentityManager.ApplicationDbContext.UserVesselPermissions(Guid.Parse(identity.GetUserId()), DateTime.Now).ToList();
var userVesselPermissionsResult = permissions.FirstOrDefault();
identity.AddClaim(userVesselPermissionsResult != null
? new Claim(SealogicalClaimUtility.VesselId, userVesselPermissionsResult.VesselId.ToString())
: new Claim(SealogicalClaimUtility.VesselId, ""));
IdentityManager.SignInManager.AuthenticationManager.SignIn(identity);
return RedirectToAction("Index", "HomeDashboard", new {area = "Dashboard"});
case SignInStatus.Failure:
ModelState.AddModelError("", "Login failed, username or password is incorrect.");
break;
case SignInStatus.RequiresVerification:
ModelState.AddModelError("", "Your account needs verification");
break;
case SignInStatus.LockedOut:
ModelState.AddModelError("",
"Login failed, this account has been locked. Contact administrator for further information.");
break;
case SignInStatus.EmailNotConfirmed:
ModelState.AddModelError("",
"Login failed, activate this account by clicking the email sent to your email account.");
break;
default:
ModelState.AddModelError("",
"Login failed, unkwon error. Contact administrator for further information");
break;
}
}
}
return View("Login", loginViewModel);
}
I am using PasswordSignInAsync() method for login validation because I need to get back that SignInStatus in order show a proper message to the user.
After that I read this question in here and they say to create a ClaimsIdentity and then use again the AuthenticationManager to sign in with that ClaimsIdentity object as I did here:
case SignInStatus.Success:
var identity = await IdentityManager.UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
var permissions = IdentityManager.ApplicationDbContext.UserVesselPermissions(Guid.Parse(identity.GetUserId()), DateTime.Now).ToList();
var userVesselPermissionsResult = permissions.FirstOrDefault();
identity.AddClaim(userVesselPermissionsResult != null
? new Claim(SealogicalClaimUtility.VesselId, userVesselPermissionsResult.VesselId.ToString())
: new Claim(SealogicalClaimUtility.VesselId, ""));
IdentityManager.SignInManager.AuthenticationManager.SignIn(identity);
return RedirectToAction("Index", "HomeDashboard", new {area = "Dashboard"});
SealogicalClaimUtility as a static class as below:
public static class SealogicalClaimUtility
{
public const string VesselId = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/vesselid";
}
Until now everything is working fine but when I try to get back that Claim it does not exist at all.
To get back that clam I use a custom property as below:
public static Guid? UserSelectedVesselId
{
get
{
var hasVesselClaim = ((ClaimsIdentity) HttpContext.Current.User.Identity)
.HasClaim(claim => claim.Type == SealogicalClaimUtility.VesselId);
if (hasVesselClaim)
{
var vesselClaim = ((ClaimsIdentity) HttpContext.Current.User.Identity).Claims.FirstOrDefault(
claim => claim.Type == SealogicalClaimUtility.VesselId);
if (vesselClaim != null)
{
Guid vesselId;
if (Guid.TryParse(vesselClaim.Value, out vesselId))
{
return vesselId;
}
}
return null;
}
var currentUserPermission = UserVesselPermissions.FirstOrDefault();
return currentUserPermission?.VesselId;
}
}
Any idea what I am doing wrong in here ?
Thanks in advance!
I am using ASP.NET Identity version 2

Related

asp.net mvc multitenant database per login error

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 many different databases simultaneously. When users from more 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 many 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:
My applicatinon dbcontext is
public static string _DbName;
public ApplicationDbContext() : base(string.IsNullOrWhiteSpace(ConString.dbCatlogConn) ? "DefaultConnection" : ConString.dbCatlogConn, throwIfV1Schema: false)
{
this.Database.CommandTimeout = 600;
}
public ApplicationDbContext(string dbName) : base(dbName)
{
_DbName = dbName;
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
Login class and database initializer in account controller
if (!ModelState.IsValid)
{
return View(model);
}
var regx = new Regex("[-]");
if (!regx.IsMatch(userdash))
{
ViewBag.Message = "Error";
return View();
}
string x = model.UserName.ToLower();
Session["Initial"] = x.Split('-').First();
var dbcompanies = db.CompanyLists.Where(t => (t.InitializationLetters == Session["Initial"].ToString())).Select(t => new { ConnString = t.ConnectionString }).ToList();
if (dbcompanies.Count > 0)
{
if (dbcompanies[0].ConnString == "")
{
}
else
{
Session["ConnectionString"] = dbcompanies[0].ConnString;
}
}
else
{
ModelState.AddModelError("", language == "en" ? "Invalid login attempt." : "text here");
return View(model);
}
if (Session["ConnectionString"].ToString() == "" || Session["ConnectionString"].ToString() == null)
{
ViewBag.Message1 = "notfound";
return View();
}
else
{
using ( db = new ApplicationDbContext(Session["ConnectionString"].ToString()))
{
UserManager.PasswordHasher = new CustomPasswordHasher();
var user = db.Users.Where(e => e.UserName.ToLower() == model.UserName.ToLower()).FirstOrDefault();
var result = new SignInStatus();
if (user == null)
{
result = SignInStatus.Failure;
}
else
{
string dbPassword = dal.DecryptPassword(user.AnotherUsername, user.AnotherSalt, user.PasswordHash);
var status = UserManager.PasswordHasher.VerifyHashedPassword(dbPassword, model.Password);
if (status == PasswordVerificationResult.Success)
{
result = await SignInManager.PasswordSignInAsync(model.UserName, user.PasswordHash, model.RememberMe, shouldLockout: false);
}
else
result = SignInStatus.Failure;
}
switch (result)
{
case SignInStatus.Success:
if (user != null)
{
if (user.Disabled == true)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
ModelState.AddModelError("", language == "en" ? "Invalid login attempt." : "text");
return View(model);
//return View("Lockout");
}
else
{
var ip = Request.ServerVariables["REMOTE_ADDR"];
user.LastIpAddress = ip;
db.SaveChanges();
if (User.IsInRole(UsersTypes.Interview.ToString()))
{
return RedirectToRoute("profile", new { type = profile.Acceptance });
}
}
}
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("", language == "en" ? "Invalid login attempt." : "error");
return View(model);
}
}
}
I have found the same problem here asp.net mvc multitenant database per tenant
when I run i got error DbName cant to be null where should I declare or pass the database connection for that variable and will it be the main database?
I have also used the string literal as one of the comments like this one
private const string DbName = "default app connection string";
but I got error It must be variable or indexer
can I find help for that problem

SignInManager.PasswordSignInAsync always return Failure after change database connection dynamically

I have asp.net MVC code first project I have managed to change my connection string dynamically depend on login values ( the database is a copy of the main one) and correctly changed but I always get password UserManager.PasswordHasher.VerifyHashedPassword error did not match and if I neglect this code SignInManager.PasswordSignInAsync always return Failure despite it is login correctly with the same main database
this is my trial ApplicationDbContext
public ApplicationDbContext(string connectionstring)
: base(connectionstring)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext(ConString.MeqyasdbCon(ConString.dbCatlogConn));
}
login page
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl, string language = Config.DefaultLanguage)
{
string x = model.UserName.ToLower();
string dbName = x.Split('_').First();
var dbname = db.dbLists.Where(t => (t.InitializationLetters == dbName)).Select(t => new { ConnString = t.ConnectionString }).ToList();
ConString.dbCatlogConn = dbname[0].ConnString;
//new conection
var checkconnection = ConString.dbCatlogConn;
using (db = new ApplicationDbContext(ConString.dbCatlogConn))
{
bool CheckConnResult = db.Database.Exists();
//code here
var user = db.Users.Where(e => e.UserName.ToLower() == model.UserName.ToLower()).FirstOrDefault();
var result = new SignInStatus();
if (user == null)
result = SignInStatus.Failure;
else
{
string dbPassword = dal.DecryptPassword(user.AnotherUsername, user.AnotherSalt, user.PasswordHash);
// I had to fix dbPassword to check where is the error
var status = UserManager.PasswordHasher.VerifyHashedPassword("Admin", model.Password);
if (status == PasswordVerificationResult.Success)
// error here
result = await SignInManager.PasswordSignInAsync(model.UserName, user.PasswordHash, model.RememberMe, shouldLockout: false);
// result = SignInStatus.Success;
else
result = SignInStatus.Failure;
}
switch (result)
{
case SignInStatus.Success:
if (user != null)
{
if (user.Disabled == true)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
ModelState.AddModelError("", language == "en" ? "Invalid login attempt." : "error ");
// rest the connection to default
ConString.dbCatlogConn = ConString.MainConn;
return View(model);
//return View("Lockout");
}
else
{
user.LastLoginDateUtc = DateTime.Now.ToUniversalTime();
db.SaveChanges();
if (User.IsInRole(UsersTypes.Interview.ToString()))
{
return RedirectToRoute("tutrial");
}
}
}
// returnUrl = "/"; // error it shouldn't be like this alaa mohamed
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("", language == "en" ? "Invalid login attempt." : "error ");
ConString.dbCatlogConn = ConString.MainConn;
return View(model);
}
}
}

Asp net Core Identity token authentication expiration

I use asp net core Identity. This is what I am trying to do and I have no Idea how to do this so need some expert help on this. When a new user registers in my application , a password reset link is sent to the person's email, Now when the link token in the link has expired and the user clicks on the link I need to show a message as token expired. How do I find out If the token has expired or not when the user clicks on the link which is in the email. Can any one suggest ways to achieve this scenario?
The ASP.NET Core has a built-in Claims system to store information about the user .
To do that ,
add a helper method to store the expiration datetime in Register.cshtml.cs:
private async Task AddTokenExpirationInfo(IdentityUser user, int span=1*24*60)
{
var expiresAt = DateTime.Now.Add(TimeSpan.FromMinutes(span));
var tokenExpiredAtClaim = new Claim("ActivtationTokenExpiredAt", expiresAt.ToUniversalTime().Ticks.ToString());
await _userManager.AddClaimAsync(user, tokenExpiredAtClaim);
}
add a helper method to check whether the token has expired in the ConfirmEmail.cshtml.cs :
private async Task<bool> TokenExpiredValidate(IdentityUser user) {
var claims = (await _userManager.GetClaimsAsync(user))
.Where(c => c.Type == "ActivtationTokenExpiredAt");
var expiredAt = claims.FirstOrDefault()?.Value;
bool expired = true; // default value
if (expiredAt != null)
{
var expires = Convert.ToInt64(expiredAt);
var now = DateTime.Now.Ticks;
expired= now <= expires? false : true;
}
else {
expired = false;
}
// clear claims
await _userManager.RemoveClaimsAsync(user, claims);
return expired;
}
invoke AddTokenExpirationInfo when there's a registeration request :
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
///////////////// invoke here ////////////////////
AddTokenExpirationInfo(user);
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
invoke TokenExpiredValidate in your ConfirmEmail.cshtml.cs:
public async Task<IActionResult> OnGetAsync(string userId, string code)
{
if (userId == null || code == null)
{
return RedirectToPage("/Index");
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return NotFound($"Unable to load user with ID '{userId}'.");
}
var result = await _userManager.ConfirmEmailAsync(user, code);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':");
}
if (await TokenExpiredValidate(user))
throw new InvalidOperationException($"Token has alread expired '{userId}':");
return Page();
}
When a user registers , there' will be a record in the AspNetUserClaims table :
When a user confirmed successfully , the record will be removed . Just as a reminder , a more robust method is to use a background service to clear the expired record .

How does OAuth Work in mvc4 C#

I'm using Oauth to get users to register through Facebook etc.
I got all the dlls. but how does it work ?
This is a code i found on the internet but it doesn't work. Any ideas how it works?.. I'm lost :/
Thanks for reading this!
using DotNetOpenAuth.Messaging;
public ActionResult LogOn()
{
var openid = new OpenIdRelyingParty();
IAuthenticationResponse response = openid.GetResponse();
if (response != null)
{
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
FormsAuthentication.RedirectFromLoginPage(
response.ClaimedIdentifier, false);
break;
case AuthenticationStatus.Canceled:
ModelState.AddModelError("loginIdentifier",
"Login was cancelled at the provider");
break;
case AuthenticationStatus.Failed:
ModelState.AddModelError("loginIdentifier",
"Login failed using the provided OpenID identifier");
break;
}
}
return View();
}
[System.Web.Mvc.AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string loginIdentifier)
{
if (!Identifier.IsValid(loginIdentifier))
{
ModelState.AddModelError("loginIdentifier",
"The specified login identifier is invalid");
return View();
}
else
{
var openid = new OpenIdRelyingParty();
IAuthenticationRequest request = openid.CreateRequest(
Identifier.Parse(loginIdentifier));
// Require some additional data
request.AddExtension(new ClaimsRequest
{
BirthDate = DemandLevel.NoRequest,
Email = DemandLevel.Require,
FullName = DemandLevel.Require
});
return request.RedirectingResponse.AsActionResult();
}
}

DotNetOpenId - Open Id get some data

I'm using OpenId on a new website and am trying to get some basic information about the user, see the code below. Why is the following allways null?
var myData = response.GetExtension<ClaimsResponse>();
And the main code
[System.Web.Mvc.AcceptVerbs(HttpVerbs.Get)]
public ActionResult LogOn()
{
var openid = new OpenIdRelyingParty();
IAuthenticationResponse response = openid.GetResponse();
if (response != null)
{
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
FormsAuthentication.RedirectFromLoginPage(
response.ClaimedIdentifier, false);
var myData = response.GetExtension<ClaimsResponse>();
break;
case AuthenticationStatus.Canceled:
ModelState.AddModelError("loginIdentifier",
"Login was cancelled at the provider");
break;
case AuthenticationStatus.Failed:
ModelState.AddModelError("loginIdentifier",
"Login failed using the provided OpenID identifier");
break;
}
}
return View("Register");
}
[System.Web.Mvc.AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string loginIdentifier)
{
if (!Identifier.IsValid(loginIdentifier))
{
ModelState.AddModelError("loginIdentifier",
"The specified login identifier is invalid");
return View();
}
else
{
var openid = new OpenIdRelyingParty();
IAuthenticationRequest request = openid.CreateRequest(
Identifier.Parse(loginIdentifier));
// Require some additional data
request.AddExtension(new ClaimsRequest
{
Email = DemandLevel.Request,
FullName = DemandLevel.Request
});
return request.RedirectingResponse.AsActionResult();
}
}
http://www.dotnetopenauth.net/developers/help/the-axfetchassregtransform-behavior/

Categories

Resources