In this moment I´m try to get a List of users and checks if the user is in the BD or not
I´m using Web API Net 6 and Sql Server
This is the code
[HttpPost("login")]
public async Task<ActionResult<string>> Login(LoginDto request)
{
//In this line I´m try to get the list of users (this bottom line doesn't work)
await _context.Users.ToListAsync();
if(user.UserName != request.UserName)
{
return BadRequest("User Not Found");
}
// ...
Here the problem is that the program has been running for 1 time until it works normally but when I end the session and come back again there is an application on the 2nd time it can no longer find the user in the database. My idea then is to add that line of code that just doesn't work (I don't know if it's due to await or if it's wrong to get through ListAsync() or if it's due to the user inside the if not being connected with the _context of the database )
By the way, that user is static having declared it like this
-> public static User user = new User();
Can anyone help me with this problem or tell me better solutions on how to get data from a table
If you just want to search your Users table for a user record with the name passed in the LoginDTO instance, then you just ask it to the database context to search for that name.
var userInDb = await _context.Users.FirstOrDefaultAsync(x => x.UserName == request.UserName);
if(userInDb == null)
... not found ....
But let me understand better your problem. If you are implementing your custom authorization and verification infrastructure for users, then think twice becase is not as simple as it looks. (For example, how do you store passwords in that table?) There is a dedicated library for that from Microsoft and is called ASP.NET Identity
Related
I'm using POST in a .NET Core REST API to insert data on database.
In my client application, when the user clicks a button, I disable this button. But sometimes, because some reason, the click of the button may be faster than the function of the disable button. This way, the user can double click on the button and the POST will be sent twice, inserting the data twice.
To do POST I'm using axios on the client side. But how can I avoid this on the server side?
Handling concurrency with inserts is hard, frankly. Things like updates and deletes are relatively trivial as you can use concurrency tokens. When doing an update for instance, a WHERE clause is added to check the row that is about to be updated concurrency token value. If it doesn't match, that means it was updated since the data was last queried, and you can then implement some sort of recovery stategy.
Inserts don't work the same way because there's obviously nothing there yet to compare to. Your best bet is a somewhat convoluted strategy of assigning some id to a particular insertion. This will have to be persisted on a column in your table, and that column will need to be unique. When you display the form, you set a hidden input with a unique-ish value, such as Guid.NewGuid(). This will then be posted back when the user submits. This then gets added to your entity, and when you save it will be set on the row that's created.
Now let's say the user double-click the submit button firing off two nearly simultaneous requests. Because the same form data is being submit for both requests, the same id is present in both submissions. The one that makes it first ends up saving the record to the database, while the next will end up throwing an exception. Since the column the id is being saved to is unique, and the same id was sent for both requests, the second one will fail to save. At this point, you can catch the exception and recover some how.
My personal recommendation is to make it seamless to the user. When you hit the catch, you query the row that was actually inserted with that id, and return that id/data instead. For example, let's say this was for a checkout page and you were creating orders. You're likely going to redirect the user to an order confirmation page after completion. So, on the request that fails, you look up the order that was actually created, and then you just redirect to the order confirmation page immediately with that order number/id. As far as the user is concerned, they just went to directly to the confirmation page, and your app ended up only inserting one order. Seamless.
I have had this scenario some time ago. I created an Action Filter for it, which is using an Anti Fogery Token:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class PreventDoublePostAttribute : ActionFilterAttribute
{
private const string TokenSessionName = "LastProcessedToken";
public override void OnActionExecuting(ActionExecutingContext context)
{
var antiforgeryOptions = context.HttpContext.RequestServices.GetOption<AntiforgeryOptions>();
var tokenFormName = antiforgeryOptions.FormFieldName;
if (!context.HttpContext.Request.Form.ContainsKey(tokenFormName))
{
return;
}
var currentToken = context.HttpContext.Request.Form[tokenFormName].ToString();
var lastToken = context.HttpContext.Session.GetString(TokenSessionName);
if (lastToken == currentToken)
{
context.ModelState.AddModelError(string.Empty, "Looks like you accidentally submitted the same form twice.");
return;
}
context.HttpContext.Session.SetString(TokenSessionName, currentToken);
}
}
Simple use it on your method:
[HttpPost]
[PreventDoublePost]
public async Task<IActionResult> Edit(EditViewModel model)
{
if (!ModelState.IsValid)
{
//PreventDoublePost Attribute makes ModelState invalid
}
throw new NotImplementedException();
}
Make sure, you generate the Anti Fogery Token, see the documentation on how it works for Javascript or Angular.
If you use a relational database the simplest way is to add unique constraint to the table(s) where data is populated. If it's impossible or database isn't relational and you have single server instance you can use synchronization inside the application code, that is keep single instance of an entity to be populated into db and modify this instance quintessentially by using synchronization primitives like lock, etc. But this approach has significant drawback - it doesn't work if there multiple instance of your web application (on different servers for example). Another approach you can apply is using versioning approach - that is you can keep version of modification along with your data and do read before write into a database (in order to increment version) with turned on optimistic locking on the db side (most of dbs support this).
This answer inspired by #Christian Gollhardt answer
First you need to enable session in your stratup.cs add
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = Context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMemoryCache();
services.AddSession(options => {
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromMinutes(10);
options.Cookie.HttpOnly = true;
// Make the session cookie essential
options.Cookie.IsEssential = true;
});
and then
app.UseSession();
then your class
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class PreventDoublePostAttribute : ActionFilterAttribute
{
private const string UniqFormuId = "LastProcessedToken";
public override async void OnActionExecuting(ActionExecutingContext context)
{
IAntiforgery antiforgery = (IAntiforgery)context.HttpContext.RequestServices.GetService(typeof(IAntiforgery));
AntiforgeryTokenSet tokens = antiforgery.GetAndStoreTokens(context.HttpContext);
if (!context.HttpContext.Request.Form.ContainsKey(tokens.FormFieldName))
{
return;
}
var currentFormId = context.HttpContext.Request.Form[tokens.FormFieldName].ToString();
var lastToken = "" + context.HttpContext.Session.GetString(UniqFormuId);
if (lastToken.Equals(currentFormId))
{
context.ModelState.AddModelError(string.Empty, "Looks like you accidentally submitted the same form twice.");
return;
}
context.HttpContext.Session.Remove(UniqFormuId);
context.HttpContext.Session.SetString(UniqFormuId, currentFormId);
await context.HttpContext.Session.CommitAsync();
}
}
usage
[HttpPost]
[PreventDoublePost]
public async Task<IActionResult> Edit(EditViewModel model)
{
if (!ModelState.IsValid)
{
//PreventDoublePost Attribute makes ModelState invalid
}
throw new NotImplementedException();
}
I am using IdentityServer 4 (with sql storage) for my asp.net core API. I would like to add a "Last Accessed" field somewhere in the database that can be used to order one of my user types in a user search endpoint. Ideally this would show the last time they performed an action in the system.
If I do this in the login method it won't be accurate as it will only update when a token is initially generated. I could add a method to every endpoint that the user accessed but this doesn't feel like the right solution as it would be repeating myself.
What is the correct place and method for maintaining a database field that records the last time a user accessed the system?
For recording the user active time, you could try Middleware which will be called for every request.
Here are steps.
1.Add field to ApplicationUser which is store the last accessed time
public class ApplicationUser : IdentityUser
{
public DateTime LastAccessed { get; set; }
}
2.run command add-migration LastAccessed and update-database
3.Add middleware to update the last accessed time based on the user name
app.UseAuthentication();
app.Use(async (context, next) => {
await next.Invoke();
//handle response
//you may also need to check the request path to check whether it requests image
if (context.User.Identity.IsAuthenticated)
{
var userName = context.User.Identity.Name;
//retrieve uer by userName
using (var dbContext = context.RequestServices.GetRequiredService<ApplicationDbContext>())
{
var user = dbContext.ApplicationUser.Where(u => u.UserName == userName).FirstOrDefault();
user.LastAccessed = DateTime.Now;
dbContext.Update(user);
dbContext.SaveChanges();
}
}
});
I added an answer for a similar question which I did with a centralized solution in the identity server project (using IEventSink). We have 30+ apis and I found it better to do the solution in only one place instead of doing it in each and every api. So i added this answer here just in case anybody came across and have the same requirements as mine. This is my answer
My setup:
ASP.NET 4.5 web api (on Azure) saving data to SQL db (also on Azure)
AngularJS web front end (another Azure web site)
When a user first signs up, I show them a "getting started intro". The intro is only supposed to run once - I log the timestamp of the intro launch date as a custom field in the ASP.NET user table.
Imagine my surprise when I log in (as a user would) and see the intro TWICE.
The AngularJS front end is properly sending the "intro viewed" message to the ASP.NET api, and the api responds with a success message. However, when I look at the raw data in the db, the timestamp is most definitely NOT updated. Consequently, the user will see the intro a second time (at which point the timestamp gets recorded in the db properly).
I have a crappy workaround. After the client requests an OAuth Bearer token from my server, the client then requests user information (to decide whether or not to show the tour). Waiting 100ms and then sending the "tour viewed" message back to the server masks the issue.
I've not seen ANY other issues storing data at any point. Because our db is on Azure, I can't hook up Profiler and the built in auditing doesn't give me any clues.
Is there something about requesting the token that leaves ASP.NET identity in a funny state? And it takes a brief wait before you can write to the table? Are custom fields that extend the base Identity setup prone to problems like this? Is the UserManager possibly doing something weird in its black box?
Does anyone have suggestions for how to continue debugging this problem? Or ever hear of anything like it?
Here's the relevant code that should be updating the "tour viewed" timestamp in the db:
[HttpPost, Route("UserInfo")]
public async Task<IHttpActionResult> UpdateUserInfo(UpdateBindingModel model)
{
var currentUser = UserManager.FindById(User.Identity.GetUserId());
if (model.FirstName != null)
{
currentUser.FirstName = model.FirstName;
}
if (model.LastName != null)
{
currentUser.LastName = model.LastName;
}
if (model.SetIntroViewCompleteDate)
{
currentUser.IntroViewCompleteDate = DateTime.UtcNow;
}
if (model.SetIntroViewLaunchDate)
{
currentUser.IntroViewLaunchDate = DateTime.UtcNow;
}
if (model.SetTipTourCompleteDate)
{
currentUser.TipTourCompleteDate = DateTime.UtcNow;
}
if (model.SetTipTourLaunchDate)
{
currentUser.TipTourLaunchDate = DateTime.UtcNow;
}
IdentityResult result = await UserManager.UpdateAsync(currentUser);
if (result.Succeeded)
{
var data = new UserInfoViewModel
{
FirstName = currentUser.FirstName,
LastName = currentUser.LastName,
IntroViewLaunchDate = currentUser.IntroViewLaunchDate
};
return Ok(data);
}
return InternalServerError();
}
UPDATE ********* 4/18
I've also tried to move completely away from UserManager stuff. I've tried the following modifications (pulling the user data from a table like I would access any other data), but it still behaves the same. I'm starting to think that putting custom fields on the ApplicationUser object is a bad idea...
New db retrieve and save looks like this:
ApplicationDbContext newContext = new ApplicationDbContext();
var currentUser = await (from c in newContext.Users
where c.Email == User.Identity.Name
select c).SingleOrDefaultAsync();
//update some values
await newContext.SaveChangesAsync();
Basically the problem might be with initialization of the `UserManager' and the fact that this class works on the db context so you need to persist changes to that context. Here is an example:
var userStore = new UserStore<ApplicationUser>(new MyDbContext());
var userManager = new UserManager(userStore);
That way you remember both manager and context. Then in your method you would normally call:
IdentityResult result = await userManager.UpdateAsync(currentUser);
followed by persisting this change to db context:
var dbContext = userStore.context;
dbContext.saveChanges();
Based on your comment that waiting 100ms masks the issue, I think you may have a problem with the multiple async await calls. Try running the calls synchronously and see if you still have the same issue. My guess is that the problem might go away. My experience has been that using async await can be tricky when you have calls to asynchronous methods that call other asynchronous methods. You may have code that is executing without the proper results returned.
Well, here's what I did to solve the problem. I totally de-coupled my custom user data from the built in ASP.NET identity stuff. I've now got a separate object (and therefore separate SQL table) that stores things like FirstName, LastName, LastActiveDate, etc, etc.
This has solved my problem entirely, though it has introduced another call to the database in certain situations. I've deemed it to be not a big enough performance issue to worry about. I'm left thinking that this was some sort of weird race condition involving the generation of a token for an ASP.NET identity user then quickly writing to an Azure SQL database - lord knows what it was exactly in my code that caused the problem.
If you've got a problem that's hard to solve, often the best plan is to change the problem.
Now I need to find a meta thread discussing what to do with bounty points when you've blown up the problem...
I really looked, googled, this site, for a few days now, tried a bunch of different things, and I can't find an answer.
so, I'm trying to create a web application that will display client information after purchase. I'm using VS2012 express and C#, and I decided to use MVC4, mostly because there was a tutorial on ASP.NET that came pretty close to what I was looking to do. Any comments on my choices is not requested but also not unwelcome.
So the admin will enter all sales information at the end of each day. We record client phone numbers as account numbers in our sales protocol, so my thought was, to keep it simple, to just use the clients phone number as a login to the web application as well. Also, that way, when a client logs into the site to view the database, the database would filter automatically so that the particular client could only see their transactions.
The tutorial I followed is here.
I figured out that this is the point where the filter needs to be applied, but i'm having a lot of trouble doing so.
The controller is named "MainController"
The database is named "Main", table is "Mains"
"AccountNumber" is the field in the db that should match the Current User Id
public ActionResult Index()
{
return View(db.Mains.ToList());
}
As I understand it, I have to place [InitializeSimpleMembership] above, then grab the UserId, then define it as the filter.
First of all, you should decide one of these way:
1) keeping login and user info in a separate table and let the SimpleMembership(SM) does its default jobs.
2) Using an existing table so store users info and tell the SM which table is for.
Approach One:
To handle this approach, all you need is that you create users manually(as you do, I think) and add an extra line to the action method which is responsible of creating customers:
[HttpPost]
public ActionResult Create(Customer model)
{
if (ModelState.IsValid)
{
db.Customers.Add(model);
try
{
db.SaveChanges();
// here it is ...
WebSecurity.CreateUserAndAccount(model.Phone, model.Password);
}
catch
{
insertError = true;
}
// .. Other codes ...
}
Now, your customers can simply login to the site with their phone no. as username and that password.
And to retrieve items related to a specific user - which is currently logged into site - simply use the following query:
public ActionResult Index()
{
return View(db.Mains.Where(m => m.AccountNumber == User.Identity.Name)
.ToList());
}
If you also need approach two, tell me to update my answer and put it here
I am trying to create a site just to learn ASP.NET c#. I am stuck though trying to get values from my database, i want to say if the "customer" is logged in that i can display his address details some textboxs. Similar to a sproc using Select. Could someone assist me or recommend where i can learn how to get details of customer IF they are logged in? Or is there a particular way i should do this and not use "bad programming methods".
Using The built-in Membership class is advised:
MembershipUser m = Membership.GetUser();
if (m == null)
{
// User not logged on.
}
The User Id stored in m.ProviderUserKey, than if you are using Entity Framwork (If you don't, consider using it. its simple and handy) fletch the data about this user using:
var loggedOnUser = context.Users.First(u=>u.ProviderUserKey == m.ProviderUserKey);
if (loggedOnUser == null)
{
// Shoudn't happen. If do, the user logged in for some reason is not stored in the database.
}
Its better for you to implement CustomMembershipProvider and CustomMembershipUser derived from MembershipUser, so you can retrieve all the information about the user this way:
// Get custom user (if allready logged in)
CustomMembershipUser user = Membership.GetUser() as CustomMembershipUser;