.NET Core alternative to ThreadPool.QueueUserWorkItem - asp.net-core

I'm working on implementing the ForgotPassword functionality ie in the AccountController using ASP.NET Identity as in the standard VS 2015 project template.
The problem I'm trying to solve is that when the password reset email is sent, there is a noticeable delay in the page response. If the password recovery attempt does not find an existing account then no email is sent so there is a faster response. So I think this noticeable delay can be used for account enumeration, that is, a hacker could determine that an account exists based on the response time of the forgot password page.
So I want to eliminate this difference in page response time so that there is no way to detect if an account was found.
In the past I've queued potentially slow tasks like sending an email onto a background thread using code like this:
ThreadPool.QueueUserWorkItem(new WaitCallback(AccountNotification.SendPasswordResetLink),
notificationInfo);
But ThreadPool.QueueUserWorkItem does not exist in .NET Core, so I'm in need of some alternative.
I suppose one idea is to introduce an artificial delay in the case where no account is found with Thread.Sleep, but I'd rather find a way to send the email without blocking the UI.
UPDATE: To clarify the problem I'm posting the actual code:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await userManager.FindByNameAsync(model.Email);
if (user == null || !(await userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
var code = await userManager.GeneratePasswordResetTokenAsync(user);
var resetUrl = Url.Action("ResetPassword", "Account",
new { userId = user.Id, code = code },
protocol: HttpContext.Request.Scheme);
//there is a noticeable delay in the UI here because we are awaiting
await emailSender.SendPasswordResetEmailAsync(
userManager.Site,
model.Email,
"Reset Password",
resetUrl);
return View("ForgotPasswordConfirmation");
}
// If we got this far, something failed, redisplay form
return View(model);
}
Is there a good way to handle this using other built in framework functionality?

Just don't await the task. That's then mostly-equivalent to running all of that code on the thread-pool to start with, assuming it doesn't internally await anything without calling ConfigureAwait(false). (You'll want to check that, if it's your code.)
You might want to add the task to some set of tasks which should be awaited before the server shuts down, assuming there's some appropriate notion of "requested shutdown" in ASP.NET. That's worth looking into, and would stop the notification from being lost due to unfortunate timing of the server being shut down immediately after sending the response but before sending the notification. It wouldn't help in the case where there are problems in sending the notification though, e.g. your mail server is down. At that point, the user has been told that the email is on its way, before you can really guarantee that... just something to think about.

Related

SignalR, how to ensure that only one user can edit a given form at a time?

I have a dashboard with a list of items and a finite number of users. I want to show "an item is being edited" near said item to avoid simultaneous edits and overwrites of data.
This seems to me like updating a flag in the database and relatively simple signalr implementation with the javascript simply adding/removing a css class.
I have seen this:
Prevent multiple people from editing the same form
which describes a method with posting every X minutes and clearing the flag from the database when there are no more update messages from the user.
The issue is:
I was wondering if there was a signalr method (like disconnect; i know it exists but I don't know if it fits this scenario) to do that elegantly rather than running a timer function. If so, is it possible for the server to miss the event and permanently leave the flagged as "editing" when it is not?
you could implement a hub for this, here is a example:
public class ItemAccessHub : Hub
{
public override Task OnConnectedAsync()
{
// your logic to lock the object, set a state in the db
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
// your logic to unlock the object
return base.OnDisconnectedAsync(exception);
}
}
to get information from the query you can access the HttpContext:
Context.GetHttpContext().Request.Query.TryGetValue("item-id", out var itemId)
so you could start a connection when the user is accessing the form and send the id of the item in the query:
/hub/itemAccess?item-id=ITEM_ID
and when the user closes the form then disconnect the connection.
with this method the item is also unlocked when the user loses his network connection.
the on disconnect method is allays invoked when a client disconnects, so you can do your clean up in this method.
in this hub you can than also implement the update function
i hope this is what you are looking for

HttpContext.Session in Blazor Server Application

I am trying to use HttpContext.Session in my ASP.NET Core Blazor Server application (as described in this MS Doc, I mean: all correctly set up in startup)
Here is the code part when I try to set a value:
var session = _contextAccessor.HttpContext?.Session;
if (session != null && session.IsAvailable)
{
session.Set(key, data);
await session.CommitAsync();
}
When this code called in Razor component's OnAfterRenderAsync the session.Set throws following exception:
The session cannot be established after the response has started.
I (probably) understand the message, but this renders the Session infrastructure pretty unusable: the application needs to access its state in every phase of the execution...
Question
Should I forget completely the DistributedSession infrastructure, and go for Cookies, or Browser SessionStorage? ...or is there a workaround here still utilizing HttpContext.Session? I would not want to just drop the distributed session infra for a way lower level implementation...
(just for the record: Browser's Session Storage is NOT across tabs, which is a pain)
Blazor is fundamentally incompatible with the concept of traditional server-side sessions, especially in the client-side or WebAssembly hosting model where there is no server-side to begin with. Even in the "server-side" hosting model, though, communication with the server is over websockets. There's only one initial request. Server-side sessions require a cookie which must be sent to the client when the session is established, which means the only point you could do that is on the first load. Afterwards, there's no further requests, and thus no opportunity to establish a session.
The docs give guidance on how to maintain state in a Blazor app. For the closest thing to traditional server-side sessions, you're looking at using the browser's sessionStorage.
Note: I know this answer is a little old, but I use sessions with WebSockets just fine, and I wanted to share my findings.
Answer
I think this Session.Set() error that you're describing is a bug, since Session.Get() works just fine even after the response has started, but Session.Set() doesn't. Regardless, the workaround (or "hack" if you will) includes making a throwaway call to Session.Set() to "prime" the session for future writing. Just find a line of code in your application where you KNOW the response hasn't sent, and insert a throwaway call to Session.Set() there. Then you will be able to make subsequent calls to Session.Set() with no error, including ones after the response has started, inside your OnInitializedAsync() method. You can check if the response is started by checking the property HttpContext.Response.HasStarted.
Try adding this app.Use() snippet into your Startup.cs Configure() method. Try to ensure the line is placed somewhere before app.UseRouting():
...
...
app.UseHttpsRedirection();
app.UseStaticFiles();
//begin Set() hack
app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
//this throwaway session variable will "prime" the Set() method
//to allow it to be called after the response has started
var TempKey = Guid.NewGuid().ToString(); //create a random key
Context.Session.Set(TempKey, Array.Empty<byte>()); //set the throwaway session variable
Context.Session.Remove(TempKey); //remove the throwaway session variable
await Next(); //continue on with the request
});
//end Set() hack
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
...
...
Background Info
The info I can share here is not Blazor specific, but will help you pinpoint what's happening in your setup, as I've come across the same error myself. The error occurs when BOTH of the following criteria are met simultaneously:
Criteria 1. A request is sent to the server with no session cookie, or the included session cookie is invalid/expired.
Criteria 2. The request in Criteria 1 makes a call to Session.Set() after the response has started. In other words, if the property HttpContext.Response.HasStarted is true, and Session.Set() is called, the exception will be thrown.
Important: If Criteria 1 is not met, then calling Session.Set() after the response has started will NOT cause the error.
That is why the error only seems to happen upon first load of a page--it's because often in first loads, there is no session cookie that the server can use (or the one that was provided is invalid or too old), and the server has to spin up a new session data store (I don't know why it has to spin up a new one for Set(), that's why I say I think this is a bug). If the server has to spin up a new session data store, it does so upon the first call to Session.Set(), and new session data stores cannot be spun up after the response has started. On the other hand, if the session cookie provided was a valid one, then no new data store needs to be spun up, and thus you can call Session.Set() anytime you want, including after the response has started.
What you need to do, is make a preliminary call to Session.Set() before the response gets started, so that the session data store gets spun up, and then your call to Session.Set() won't cause the error.
SessionStorege has more space than cookies.
Syncing (two ways!) the sessionStorage is impossible correctly
I think you are thinking that if it is on the browser, how can you access that in C#? Please see some examples. It actually read from the browser and transfers (use) on the server side.
sessionstorage and localstorage in blazor are encrypted. We do not need to do extra for encryption. The same applies for serialization.

MVC4 Razor Update View in Real time

I'm a relative noob programmer so apologies in advance!
I am writing using MVC4 and Razor and have a user selection view that can potentially lead to many web calls being made from the controller. I want to update the result view
in real-time as these web service calls return, at the minute though it seems that the controller waits until all the web calls are finished before updating the model
and rendering the result view. We have attempted to use async and await to improve things with only marginal success.
Other complications I have are that we are using an in house framework and performing other operations once the webcall returns e.g. logging \ occasional db access, all of which
lead to the controller action taking a substantial period.
To put it in context this is a monitoring application so depending on the user selections we may have 0 -> several hundred internal web service calls made on the click of a button.
So essentially I am wondering how this is best handled e.g. realtime updating of an MVC4 view where you have multiple individual web service calls, some of them potentially
lengthy e.g. up to 60seconds max.
My thoughts on how to improve things were
use SignalR and at the point where the individual webservices respond, broadcast an update to the result screen ~ however I still have the issue that the code is waiting for
my controller call to the webservices to finish before rendering the view.
to avoid wait on controller method e.g. 'output = await pc.CallApproriateSCs(selectedServiceInfoDetails);' perhaps pass all the calls off to some internal stack and have a timer
pop them and process them on a different thread in another class, this could then free my controller to display a defaulted result view immediately, letting SignalR update it in
real-time
it all seems a bit like using a sledgehammer to crack a walnut though, surely MVC4 has some nicer way of handling this scenario?
Thanks in advance,
N
public virtual async Task<ActionResult> MultipleCommand(ManualSelectionVM model)
{
var viewObject = new ManualSelectionResultVM();
if (model != null)
{
var input = new ServiceInfo();
//Retrieving serviceInfo object details from cache based on user selection, they are unique
List<ServiceInfo> selectedServiceInfoDetails = GetServiceInfoDetailsFromCache(GetSelectedServiceInfoIDs(model));
List<ServiceOutputCdt> output = new List<ServiceOutputCdt>();
IGenericPC pc = null;
try
{
pc = PCFactory.Create<IGenericPC>();
output = await pc.CallApproriateSCs(selectedServiceInfoDetails);
viewObject = ConvertServiceOutputIntoVM(output);
}
finally
{
AICS.ARCHITECTURE.SERVICES.CLEANUP.CleanupSVC.Cleanup(pc);
}
}
return this.View(MVC.ManualSelection.Views.Result, viewObject);
}

How does AntiForgeryToken, specifically, 'match up' with a cookie?

We have a web page, and it has a form on it. There is an antiforgerytoken added to this MVC page's form via the helper:
#Html.AntiForgeryToken()
We realised today that we have the page cached, which we thought would be a massive issue, but multiple machines may submit the same form, even though they share the same verification token in the page's source!?
This is unexpected as far as I'm aware, I thought the idea was that the same verification token can be found in the cookie? I'm obviously misunderstanding the mechanism behind the token (both on the page and the amendment of a user's cookie).
Can somebody explain to me how this page still works? More specifically, how the server validates the form's post request.
I thought it was as simple as the server checking that the token string was identical to a token string found in the cookie.
NB: Caching is turned off for now fwiw, we're not 100% happy with caching a page with tokens on it now that we're a bit more clued up on it.
We realised today that we have the page cached, which we thought would be a massive issue, but multiple machines may submit the same form, even though they share the same verification token in the page's source!?
Yes, caching is a problem when it comes to anti-forgery tokens. You have two choices:
Don't cache the form.
Use the VaryByCustom property of OutputCache to vary by the token itself.
I don't have code to hand with me, so I'll show you an example which is taken from this article:
In order to avoid this issue you can use the VaryByCustom property on the OutputCache attribute:
[OutputCache(
Location = OutputCacheLocation.ServerAndClient,
Duration = 600,
VaryByParam = "none",
VaryByCustom = "RequestVerificationTokenCookie")]
public ActionResult Index()
{
return new View();
}
And then program the rule on the global.asax‘s GetVaryByCustomString method:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom.Equals("RequestVerificationTokenCookie", StringComparison.OrdinalIgnoreCase))
{
string verificationTokenCookieName =
context.Request.Cookies
.Cast<string>()
.FirstOrDefault(cn => cn.StartsWith("__requestverificationtoken", StringComparison.InvariantCultureIgnoreCase));
if (!string.IsNullOrEmpty(verificationTokenCookieName))
{
return context.Request.Cookies[verificationTokenCookieName].Value;
}
}
return base.GetVaryByCustomString(context, custom);
}
Edit per comments
The reason it works is because it's only comparing the value of the cookie to the value of the generated hidden field on the form. There isn't a list of tokens maintained on the server, ready to be validated, if you thought it might work that way. That means, as far as the client is concerned, despite the generation of the form being cached on the server, the client is still receiving a 'new' form and, consequently, a cookie to go along with that, so the comparison won't fail.

Windows Phone and Website Request

I wish to send a request to a Website (server) to update a potential scoreboard which everyone who has the application can see. This website isn't of course restricted to just the application users - but it can be accessible to anyone.
Is it possible to call a WCF from a Windows Phone app for example where the WCF can then update the database. So whenever someone goes on the website, the updated changes will be seen.
Is this at all possible? And if it is, would this be the most sensible/optimised way of doing things?
I did this using a BsckgroundWorker in my ViewModel to prevent hanging the UI. This way, you can "set it and forget it". Something like this:
private void UpdateScoreboard()
{
var scoreboardWorker = new BackgroundWorker();
scoreboardWorker.DoWork += (s,dwe)=>{
//call some WCF service compleate async
};
scoreboardWorker.RunWorkerCompleted += (s,rwe)=>{
// check whether rwe.Error is not null, indicating an exception. Show an alert maybe
};
scoreboardWorker.RunWorkerAsync();
}