I am using Microsoft.AspNetCore.Identity to implement basic system Login/Logout.
Here is a part of my code:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginVM login, string returnUrl=null)
{
if (!ModelState.IsValid)
return View(login);
var foundUser = await _userManager.FindByEmailAsync(login.EmailAddress);
if (foundUser != null)
{
if (!await _userManager.IsEmailConfirmedAsync(foundUser))
{
ViewBag.Message = "Your credentials are wrong or your account is not activated.";
return View(login);
}
}
else return RedirectToAction("Register", "Account");
var result = await _signinManager.PasswordSignInAsync(
login.EmailAddress,
login.Password,
login.RememberMe,
false);
if (!result.Succeeded)
{
ModelState.AddModelError("Login error - please try again or contact support team.", "");
return View(login);
}
if (string.IsNullOrWhiteSpace(returnUrl))
return RedirectToAction("Index", "Home");
return Redirect(returnUrl);
}
It works pretty good although, I can see my passwords on Headers through Network tab(Google Chrome developer tools).
Is there a way to avoid that? Can those credentials been hidden somehow?
Any help is welcome
When submitting a form, the input data will be visible to the user entering it. However, it will be encrypted in transit if you use https.
Technichally you can use javascript to obfuscate it, but it will not increase security.
Related
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();
}
so i use google authentication to validate the users on our system
here is my code
public IActionResult Login()
{
System.IO.File.WriteAllText("log.txt", Url.Action("ExternalLoginCallback"));
return new ChallengeResult(
GoogleDefaults.AuthenticationScheme,
new AuthenticationProperties
{
RedirectUri = Url.Action("ExternalLoginCallback")
});
}
public IActionResult ExternalLoginCallback()
{
System.IO.File.AppendAllText("log.txt", "I am redirected");
var authenticateResult = HttpContext.AuthenticateAsync("External").Result;
if (!authenticateResult.Succeeded)
return BadRequest(); // TODO: Handle this better.
var claimsIdentity = new ClaimsIdentity("Application");
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.Email));
var identity = authenticateResult.Principal.Identities.FirstOrDefault();
if (identity != null)
{
var emailClaim = identity.Claims.FirstOrDefault(c => c.Type.ToLower().Contains("emailaddress"));
if (emailClaim == null)
{
return BadRequest();
}
var emailData = emailClaim.Value;
if (string.IsNullOrEmpty(emailData))
{
return BadRequest();
}
var connectionString = _config.GetConnectionString("DefaultConnection");
UserBL userBL = new UserBL(connectionString);
var userModel = userBL.GetUserData(emailData);
if(userModel == null || userModel.HasError)
{
return BadRequest();
}
HttpContext.Session.SetString("UserData", JsonConvert.SerializeObject(userModel.Data));
if (userModel.Data.UserRoles.Any(r => r.Id == (int)UserRolesEnum.ProductOwner
|| r.Id == (int)UserRolesEnum.CopyEditor))
{
return RedirectToAction("Index", "Home");
}
else
{
return RedirectToAction("List", "Translation");
}
return RedirectToAction("UnAuthorized");
}
return BadRequest();
}
it worked fine users were redirected to google to enter their credentials and after that google redirects them back to the website till couple of days back google stopped redirecting back to the website and stays stuck on https://accounts.google.com.eg/accounts/SetSID
any pointers to how i can debug this or how to solve it
any help would be appreciated
Finally after a week i found the problem, it was not in the code it was the server.
The IIS limits the query string character count (i think the default is 260 character) and in my case google responds with a query string with 544 character in which the IIS does not accept or respond to so it stays stuck on the SetSID page
Here is where i found the solution to increase the query string max length
Increase max url length in asp.net core
as of why it was working and then stopped this i could not figure it out
What I have in my mind when navigating through the application, I want to save the token to the localhost along with role name and I will check if the users have access to a certain link. Is that how it works? with Authgard in Angular 8?. Can you give me some insight of navigating an application with the role from Identity(which is built in from ASP.net core 3.1).
login
// POST api/auth/login
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if (identity == null)
{
//return null;
return BadRequest(Error.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
}
var jwt = await Tokens.GenerateJwt(identity, _jwtFactory, credentials.UserName, _jwtOptions, new JsonSerializerSettings { Formatting = Formatting.Indented });
return new OkObjectResult(jwt);
}
Generate Token Method
public static async Task<string> GenerateJwt(ClaimsIdentity identity, IJwtFactory jwtFactory, string userName, JwtIssuerOptions jwtOptions, JsonSerializerSettings serializerSettings)
{
var response = new
{
id = identity.Claims.Single(c => c.Type == "id").Value,
//probably here I want to send the role too!!
auth_token = await jwtFactory.GenerateEncodedToken(userName, identity),
expires_in = (int)jwtOptions.ValidFor.TotalSeconds
};
return JsonConvert.SerializeObject(response, serializerSettings);
}
}
You need to add claims information when generating your JWT.
Here`s an example
And another one:
1 part(how to implement JWT), 2 part(about claims here)
We built a Xamarin.Forms app that operates as an IdentityServer4 client using the WebView implementation approach, which worked perfectly. However, Apple will not accept our app in the store because they want the authentication and new account creation to occur within the app. Therefore, I've been trying to post the credentials entered within the app's login screen to the IdentityServer with the hope that I can simulate the same process that occurs when a user enters their credentials and submits (posts) the login page:
public async Task<IActionResult> Authenticate([FromBody] UserLoginViewModel model)
{
String result = String.Empty;
try
{
if (!string.IsNullOrWhiteSpace(model.UserName))
{
UserKey user = await _userManager.FindByNameAsync(model.UserName.ToLowerInvariant());
if (String.IsNullOrEmpty(user?.SSID))
{ result = AuthenticateResultEnum.UserDoesNotExist.ToString(); }
else
{
var signin_result = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberLogin, false);
if (signin_result.Succeeded)
{
if (_interactionService.IsValidReturnUrl(model.ReturnUrl))
{
LocalRedirect(model.ReturnUrl);
}
}
else if (signin_result.IsLockedOut)
{ result = AuthenticateResultEnum.AccountLocked.ToString(); }
}
}
}
catch (Exception ex)
{
result = AuthenticateResultEnum.Error.ToString();
_logger.ExceptionAsync(ex);
}
return new JsonResult(result);
}
However, I've had no luck with this process because of all the redirects involved. Is there a well-established way of authenticating a user with IdentityServer without showing a login page, but instead posting the credentials and Authorization Request from the apps login form? Other well-known apps appear to be able to do so, I'm just seeking confirmation that it can be done with IdentityServer4.
Thank you,
Andrew
I have created an MVC 4 application which targets .NET 4.0. After deploying to my production server, it will show the login page but will not redirect to the default page. However, when I add debugging, I can see that the authentication process works but then the error I am getting is an error that says it can't find my View for my Error Page and then shows my Error Page. It just seems that it will not go to my "Home/Index" page - even when I remove the authorize attribute. Of course the application works in development. Additionally, it will not go to my register page or forgot login page.
My Login Controller looks like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiforgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if(ModelStat.IsValid && _userService.Login(model.UserId, model.Password))
{
var user = _userService.GetUser(model.UserId);
var loggedInUser = new LoggedInUser
{
// Build the user for custom IPrincipal
};
var userData = JsonConvert.SerializeObject(loggedInUser);
var compressData = StringCompression.Compress(userData);
var authTicket = new FormsAuthenticationTicket(
1,
user.UserId,
DateTime.Now,
DateTime.Now.AddHours(1),
false,
compressData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
if(encTicket != null)
{
var faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
{
HttpOnly = true
};
Response.Cookies.Add(faCookie);
}
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
_userService.UpdateUser(user);
_uow.Commit();
return Url.IsLocalUrl(returnUrl) ? (ActionResult)Redirect(returnUrl) : RedirectToAction("Index", "Home");
}
return View(model);
and in my Global.asax:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if(authCookie != null)
{
var decompressedData = StringCompression.Decompress(authTicket.UserData);
var loggedInUser = JsonConvert.DesrializeObject<LoggedInUser>(decompressedData);
var currrentUser = new CustomPrincipal(authTicket.Name)
{
// Build the CustomPrincipal from the loggedInUser
};
if(HttpContext.Current.User.Identity.IsAuthenticated)
{
HttpContext.Current.User = currentUser;
}
}
}
I hope that this is enough to give someone an idea of what I may be doing wrong. Somehow I feel that it is something small that I am missing. Thanks in advance.
~InDireStraits
Update:
After more troubleshooting, it would seem that the issue may have something to do with the fact that I am using a BaseController for specifying permissions but I am still baffled as to why the application works as intended in my development environment but not in production. To verify my IIS settings I installed the default MVC4 App to production which does not have .NET 4.5, and it runs. I am using VS 2012 so I do have 4.5. Could I somehow be introducing .NET 4.5 classes or functionality even if this targets .NET 4.0? At any rate, here is my BaseController code:
public class BaseController: Controller
{
private string _actionKey;
private const string PermisisionList = "permissionList";
private Dictionary<string, string> _requiredActionPermissions;
private static readonly IControllerActionService<ControllerAction> _actionService;
protected new CustomPrincipal User
{
get
{
return HttpContext.User as CustomPrincipal;
}
}
public BaseController(IControllerActionService<ControllerAction> actionService)
{
_actionService = actionService;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check to see if the PermissionList is loaded and load if necessary
if(!CacheLayer.Exists(PermissionList))
{
_requiredActionPermissions = _actionService.GetControllerActionDictionary();
CacheLayer.Add(_requiredActionPermissions, PermissionList);
}
else
{
_requiredActionPermission = CacheLayer.Get<Dictionary<string, string>>(PermissionList);
}
// Get the Controller/Action of the current request
_actionKey = string.Format("{0}-{1}", filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor.ActionName);
// If the user is authenticated, grab the permissions
if(filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var userPermissions = User.Permissions;
if(!_requiredActionPermissions.Values.Any(a=>a.Equals(_actionKey, StringComparison.OrdinalIgnoreCase)))
{
return;
}
if(userPermissions.Contains(_requiredActionsPermissions.FirstOrDefault(x=>x.Value == _actionKey).Key))
{
return;
}
filterContext.Result = new RedirectResult("~/Error/ErrorUnauthorized");
return;
}
if(!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if(!_requiredActionPermissions.Values.Any(a=>a.Equals(_actionKey, StringComparison.OrdinalIgnoreCase)))
{
return;
}
}
if(filterContext.HttpContext.Request.Url == null)
{
return;
}
if(filterContext.HttpContext.Request.Url.AbsolutePath == FormsAuthentication.LoginUrl)
{
return;
}
var redirectUrl = string.Format("?returnUrl={0}", filterContext.HttpContext.Request.Url.PathAndQuery);
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + redirectUrl, true);
}
UPDATE 2: Installed .NET 4.52 on Staging Server and the application now works as intended. The problem is that I will not be able to install this on the production server. I don't understand what it is that 4.5 is fixing that 4.0 does not seem to facilitate. HELLLLLLPPP!!!!!!
The answer can be found here. To summarize, I added an extra parameter to my route config that worked in 4.5 but not in 4.0. Will follow up on the linked question. Thanks