I am new to c# and .net api. I am doing a login method where if a user is found in database, it will return the user.The user object is likethis,
{ email: user#email.com, password: password, name: User Name, }
First thing, I want to remove the password from the return object, second I want to add the JWT token to the return object. Here is my code:
public object LoginCurrentUser(User user) {
var result = AuthenticateUser(user);
if (result != null)
{
var token = Generate(user);
//I want to create new variable here that removes the password and adds the token to the field.
return result;
}
else {
return null;
}
}
It should be pretty straight forward.
if (result != null)
{
var token = Generate(user);
var response = new {
Email = result.Email,
Name = result.Name,
Token = token
};
return response;
}
Alternatively, You can create a DTO class for the response and return its object.
Related
This API is intended for a mobile application. The goal is to let the user confirm the email upon registration. When the user registers, a confirmation link is generated and sent over the email. I've done it the same way in a MVC project, it worked fine, but in a Web API project looks like it ain't gonna cut.
Now when the user clicks that link, the respective action method should be hit and do the job.
The only problem is, the ConfirmEmail action method is just not getting triggered when clicking the confirmation link although it looked fine.
Here are the main configurations which might help
MVC service configuration
services.AddMvc(options =>
{
options.EnableEndpointRouting = true;
options.Filters.Add<ValidationFilter>();
})
.AddFluentValidation(mvcConfiguration => mvcConfiguration.RegisterValidatorsFromAssemblyContaining<Startup>())
.SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_3_0);
Identity Service
public async Task<AuthenticationResult> RegisterAsync(string email, string password)
{
var existingUser = await _userManager.FindByEmailAsync(email);
if(existingUser != null)
{
return new AuthenticationResult { Errors = new[] { "User with this email address exists" } };
}
// generate user
var newUser = new AppUser
{
Email = email,
UserName = email
};
// register user in system
var result = await _userManager.CreateAsync(newUser, password);
if (!result.Succeeded)
{
return new AuthenticationResult
{
Errors = result.Errors.Select(x => x.Description)
};
}
// when registering user, assign him user role, also need to be added in the JWT!!!
await _userManager.AddToRoleAsync(newUser, "User");
// force user to confirm email, generate token
var token = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
// generate url
var confirmationLink = _urlHelper.Action("ConfirmEmail", "IdentityController",
new { userId = newUser.Id, token = token }, _httpRequest.HttpContext.Request.Scheme);
// send it per email
var mailresult =
await _emailService.SendEmail(newUser.Email, "BingoApp Email Confirmation",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(confirmationLink)}'>clicking here</a>.");
if (mailresult)
return new AuthenticationResult { Success = true };
else
return new AuthenticationResult { Success = false, Errors = new List<string> { "Invalid Email Address"} };
}
Controller
[HttpPost(ApiRoutes.Identity.Register)]
public async Task<IActionResult> Register([FromBody] UserRegistrationRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(new AuthFailedResponse
{
Errors = ModelState.Values.SelectMany(x => x.Errors.Select(xx => xx.ErrorMessage))
});
}
// register the incoming user data with identity service
var authResponse = await _identityService.RegisterAsync(request.Email, request.Password);
if (!authResponse.Success)
{
return BadRequest(new AuthFailedResponse
{
Errors = authResponse.Errors
});
}
// confirm registration
return Ok();
}
[HttpGet]
public async Task<IActionResult> ConfirmEmail(string userId, string token)
{
if (userId == null || token == null)
{
return null;
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return null;
}
var result = await _userManager.ConfirmEmailAsync(user, token);
if (result.Succeeded)
{
await _emailService.SendEmail(user.Email, "BingoApp - Successfully Registered", "Congratulations,\n You have successfully activated your account!\n " +
"Welcome to the dark side.");
}
return null;
}
Your _urlHelper.Action(..) looks a bit suspicious to me.
I'm not sure you should pass the full controller name, that is, including the actual word controller.
Try _urlHelper.Action("ConfirmEmail", "Identity", instead.
As a tip: I try to avoid magic strings like these by using nameof(IdentityController) because it will return the controller name without the controller postfix.
following is code for calling web API call
public async System.Threading.Tasks.Task<ActionResult> RequisitionNameByQuantityThisDraw()
{
//Guid applicationRequisitionOid
string userName = string.Empty;
SessionObject sessionData = new SessionObject().GetSessionData();
if (sessionData == null)
{
return RedirectToAction("UserLogin", "Login");
}
Guid aa = new Guid("41CF8843-2AF4-40D0-9998-D6D516367A7D");
HttpResponseMessage response = _HttpClient.GetAsync("api/ApplicationSIRMeasure/RequisitionNameByQuantity?applicationRequisitionOid=" + aa).Result;
string userJsonString = await response.Content.ReadAsStringAsync();
return Json(userJsonString, JsonRequestBehavior.AllowGet);
}
below is web API methods
public HttpResponseMessage Get(Guid applicationRequisitionOid)
{
var result = _IService.GetAll(applicationRequisitionOid);
if (result == null)
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No data found");
else
return Request.CreateResponse(HttpStatusCode.OK, result);
}
[Route("api/ApplicationSIRMeasure/RequisitionNameByQuantity/{applicationRequisitionOid:Guid}")]
public HttpResponseMessage RequisitionNameByQuantity(Guid applicationRequisitionOid)
{
Guid id = new Guid("41CF8843-2AF4-40D0-9998-D6D516367A7D");
var result = _IService.GetRequisitionByQunatityThisDraw(id);
if (result == null)
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No data found");
else
return Request.CreateResponse(HttpStatusCode.OK, result);
}
whenever I call this web API with above code it goes to first get method.
but I won't to call in a second method
The route on the Web API is
[Route("api/ApplicationSIRMeasure/RequisitionNameByQuantity/{applicationRequisitionOid:guid}")]
Yet the request is calling
"api/ApplicationSIRMeasure/RequisitionNameByQuantity?applicationRequisitionOid="
Because the url does not match the second route template it is finding a match on the other action because the query string parameter matches.
you need to update the URL being called to match the route template of the target action.
var aa = new Guid("41CF8843-2AF4-40D0-9998-D6D516367A7D");
var url = string.Format("api/ApplicationSIRMeasure/RequisitionNameByQuantity/{0}", aa);
var response = await _HttpClient.GetAsync(url);
I really do not see the need to create the Guid when you can just build the URL with the same string that was used to create the Guid instance
The following C# code accepts two parameter username and password from API using ajax
public Login[] checkLogin(models.Login log)
{
Boolean flag = false;
connection obj = new connection();
IMongoDatabase server = obj.getConnection();
var collection = server.GetCollection<models.Login>("login");
string param = "{'username':'" + log.username + "','password':'"+ log.password +"'}";
List<Login> result = new List<Login>();
var check = collection.Find(param);
foreach(var emp in check.ToList())
{
result.Add(emp);
}
if(result == null)
flag = false;
else
flag = true;
return result.ToArray();
}
I want to check the username and password from my MongoDB database. I am trying to find method but don't know how to check the value if it is available or not.
In order to test whether provided credentials are valid, your method should simply return a boolean value.
You might do something like this.
public IMongoCollection<models.Login> GetLoginCollection()
{
var client = new MongoClient();
var database = client.GetDatabase("dbName");
var collection = database.GetCollection<models.Login>("login");
return collection;
}
public bool CheckLogin(models.Login log)
{
var collection = this.GetLoginCollection();
var authSuccessful = collection
.Count(login =>
login.username == log.username &&
login.password == log.password) > 0;
return authSuccessful;
}
As an alternative, CheckLogin() method might be implemented using explictly-defined filters.
public bool CheckLogin(models.Login log)
{
var collection = GetLoginCollection();
var filter = Builders<models.Login>.Filter
.And(
Builders<models.Login>.Filter.Eq(login => login.username, log.username),
Builders<models.Login>.Filter.Eq(login => login.password, log.password));
var authSuccessful = collection.Count(filter) > 0;
return authSuccessful;
}
Note that storing clear text password within the database is a bad practice. Nobody but the user should know the actual password. One solution is storing the hashed password in the database. On authentication you can compare the hash of the provided password with your stored value. One of the most common hash functions is md5.
I'm trying to decrypt the password on the login method, but it allows to login with any password I type in, not sure why, maybe someone could help me out?
My login method in the db layer:
public string loginUser(string userName, string pass)
{
string result = "";
try
{
var mongoClient = new MongoClient("mongodb://localhost");
var database = mongoClient.GetDatabase("SearchForKnowledge");
var coll = database.GetCollection<BsonDocument>("Users");
var filter = Builders<BsonDocument>.Filter.Eq("userName", userName);
var results = coll.Find(filter).ToList().First();
if (BCrypt.Net.BCrypt.Verify(pass, results["password"].ToString()))
{
result = results["userName"].ToString();
}
}
catch (Exception ex)
{
result = "";
}
return result;
}
My user controller:
public ActionResult Login(UsersLogin form)
{
User user = new User();
UserDB udb = new UserDB();
if (!form.Username.IsEmpty())
{
udb.loginUser(form.Username, form.Password);
Session["userName"] = form.Username;
return RedirectToRoute("Home");
}
return RedirectToRoute("Login");
}
The problem is in your controller
udb.loginUser(form.Username, form.Password);
Session["userName"] = form.Username;
return RedirectToRoute("Home");
You call udb.loginUser(form.Username, form.Password);, but you never check the return value of udb.loginUser method, so your code will always redirect to the home page no matter what the user name and password are.
Based on the code of loginUser method, it will return an empty string if the login fails, so change the above three lines of code to below
var loginResult = udb.loginUser(form.Username, form.Password);
if (!string.IsNullOrEmpty(loginResult))
{
Session["userName"] = form.Username;
return RedirectToRoute("Home");
}
I try to implement forget password form in my asp.net mvc 4 project, everything works fine, but when I try to login to system with new password it told me that I have wrong password.
[HttpPost]
public ActionResult ForgetPassword(UserViewModel userModel) {
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var result = new string(
Enumerable.Repeat(chars, 8)
.Select(s => s[random.Next(s.Length)])
.ToArray());
User user = _userRepo.GetUserByEmail(userModel.Email);
if (user == null) {
ViewBag.Error = Resources.Account.userEmailNotExist;
return View(userModel);
}
String newHashedPassword = Crypto.HashPassword(result);
user.Password = newHashedPassword;
user.LastPasswordChangedDate = DateTime.UtcNow;
_userRepo.SaveChanges();
string enMessage = "Your new password: " + result;
var httpCookie = Request.Cookies["lang"];
if (httpCookie != null && httpCookie.Value == "en") {
_mailHelper.SendEmail(userModel.Email, "New password", enMessage);
}
return RedirectToAction("ConfirmPasswordChange", "Account");
}
Login form:
[HttpPost]
public ActionResult Login(UserViewModel user) {
var users = _userRepo.GetAllEntitiesWithParam("JobsDb_Users_GetByEmail", user.Email).FirstOrDefault();
...
try {
var tryLogin = WebSecurity.Login(users.Username, user.Password, true);
if (tryLogin == WebSecurity.MembershipLoginStatus.Failure)
{
var httpCookie = Request.Cookies["lang"];
if (httpCookie != null && httpCookie.Value == "en") {
ViewBag.Error = "Your password is incorrect.";
new SeoHelper().ReturnSeoTags(this, "Login");
}
return View(user);
}
...
} catch {
...
}
}
inside WebSecurity
public static MembershipLoginStatus Login(string username, string password, bool rememberMe) {
if (Membership.ValidateUser(username, password)) {
FormsAuthentication.SetAuthCookie(username, rememberMe);
return MembershipLoginStatus.Success;
} else {
return MembershipLoginStatus.Failure;
}
}
inside Membership
public override bool ValidateUser(string username, string password) {
if (string.IsNullOrEmpty(username)) {
return false;
}
if (string.IsNullOrEmpty(password)) {
return false;
}
User user = _userRepository.GetAll().FirstOrDefault(usr => usr.Username == username);
if (user == null) {
return false;
}
if (!user.IsApproved.Value) {
return false;
}
if (user.IsLockedOut.Value) {
return false;
}
String hashedPassword = user.Password;
Boolean verificationSucceeded = (hashedPassword != null && Crypto.VerifyHashedPassword(hashedPassword, password));
if (verificationSucceeded) { //here is I have false if try to login using password from forget form
user.PasswordFailuresSinceLastSuccess = 0;
user.LastLoginDate = DateTime.UtcNow;
user.LastActivityDate = DateTime.UtcNow;
} else {
int failures = user.PasswordFailuresSinceLastSuccess.Value;
if (failures < MaxInvalidPasswordAttempts) {
user.PasswordFailuresSinceLastSuccess += 1;
user.LastPasswordFailureDate = DateTime.UtcNow;
} else if (failures >= MaxInvalidPasswordAttempts) {
user.LastPasswordFailureDate = DateTime.UtcNow;
user.LastLockoutDate = DateTime.UtcNow;
user.IsLockedOut = true;
}
}
_userRepository.SaveChanges();
if (verificationSucceeded) {
return true;
}
return false;
}
First step is to open up your database and verify that the new password was actually persisted. If it has, the most likely cause is that your repository is working with stale (cached) data.
If you're using Entity Framework, this happens because the framework will, by default, cache the state of the database at the time the DbContext is created, so it is retaining your original password. You can verify this by logging in with the original password.
I am not sure but following code does not look right to me:
User user = _userRepo.GetUserByEmail(userModel.Email);
if (user == null) {
ViewBag.Error = Resources.Account.userEmailNotExist;
return View(userModel);
}
String newHashedPassword = Crypto.HashPassword(result);
user.Password = newHashedPassword;
user.LastPasswordChangedDate = DateTime.UtcNow;
_userRepo.SaveChanges();
You fetched the user from repository, make changes to user object in memory and then called SaveChanges() on the repository. Does that work in your world? How does _userRepo.SaveChanges(); knows which object has changed. Do you see correct hashed value in DB after the call? What value you see in ValidateUser() method for password? Is the hashing algorithm consistent both while generating hashed password and while verifying?
I could be wrong, if that's the case it will be good if you share little bit more of analysis around the question I asked above.