I'm writing a Windows Phone 8 app that makes use of a REST API to gather data. The API requires authentication, so whenever I try to call the REST API, I have to check whether the user is authenticated. If the user isn't authenticated, then I want to bring up a login view and let the user authenticate. It's not HTTP authentication, but it uses a custom login screen.
My problem is: I would like to use await to wait for the authentication to happen, but I don't know how to do that since I have to bring up another view. Here's pseudo-code for what I would like to do:
The LoadData method:
async Task LoadDataAsync() {
bool authenticated = await AuthenticateAsync();
if (authenticated) {
// do REST API stuff
}
}
And the AuthenticateAsync method:
async Task<bool> AuthenticateAsync() {
if (alreadyAuthenticated)
return true;
// not authenticated, so bring up a login view to let the user log in
// How do I do this in the context of async-await?
}
So in the AuthenticateAsync method I would like to bring up a login view if the user needs to authenticate. But I can't call NavigationService.Navigate() because that's not async-await friendly and I wouldn't be able to await anything.
What's the right way to do this?
I found an answer to this. I ended up using the answer here: https://stackoverflow.com/a/12858633/225560
Specifically, I used a TaskCompletionSource<bool> object, which I could then await. I then navigated to the login view, and it called me back when login was successful. I then set the result of the TaskCompletionSource, which freed up my await and let the method continue. This ended up working almost like a delegate in objective-C, in that the Login view called a method back on the initial class when login completed.
Related
I have some Blazor Server UI that's displaying some user-specific content:
<AuthorizeView>
<Authorizing>
<span class="spinner"></span>
</Authorizing>
<NotAuthorized>
not logged in
</NotAuthorized>
<Authorized>
logged in as #context.User.Identity!.Name
</Authorized>
</AuthorizeView>
I'm currently using a custom AuthenticationStateProvider to track the login session. It stores a user token in browser session storage on login and during first render it fetches the token and retrieves the session info on the backend. This much is all working fine.
If the service is recreated and the state queried before first render, since it can't access JS yet, it simply returns "not logged in" and then requeries after first render. (As far as I can tell it's not possible to keep it in the "authorizing" state by not completing the Task as this stalls the prerender.)
private readonly ProtectedSessionStorage _Storage;
private bool _HasLoaded;
public AuthorizationService(ProtectedSessionStorage storage) => _Storage = storage;
public void AfterFirstRender()
{
_HasLoaded = true;
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
if (!_HasLoaded) // prerendering
{
return new AuthenticationState(new ClaimsPrincipal());
}
// ... code to fetch the real identity from _Storage
}
The problem is that when the user clicks an ordinary local navigation link (page to page within the same app via <a href="pageX">), this appears to tear down and rebuild all the scoped services, so it loses the authentication state and has to query it again -- but since it can't do that before first render, there is always a flash of "not logged in" on every navigation before it finishes reloading the state and updates, which is ugly. (It's very fast, but still visible. It's more visible if you deliberately add a delay to the process.)
I do have prerendering enabled (as per default) but I'm surprised that it seems to be triggering a full prerender+reload on every navigation instead of preserving state. (I expect that on a full reload, or manual location edit, but not from internal navigation.)
When using WebAssembly, this just worked -- both singletons and scopes were per-session and navigations happened internally without discarding any state. Not so with Server, apparently; singletons survive but are larger than per-user or per-session, and scopes seem to be per-page, which is smaller than per-session.
Am I missing some key trick to get this to behave sensibly? Either to avoid the re-prerender on navigate or to keep scoped service states, or reload them reliably before JS render, or pass some kind of session id from pre-navigation to prerendering? The official docs basically just "this exercise is left to the reader", but without at least some kind of id preservation I don't see how it's possible.
Blazor server calls GetAuthenticationStateAsync() upon a new visit. This is the place to look for a previously saved cookie and authenticate the user accordingly. Next code will give you the clue.
First time it will silently catch an exception due to missing JS but it is immediately called a second time successfully:
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
//get claimsPrincipal from stored user if within a session
//or get it from cookie if a new session
//return AuthenticationState from claimsPrincipal
//or return anonymous if user throws error on modifying sessionstore by hand
try
{
var storedUser = await TryGetUserFromBrowserStorage();
if (storedUser == null) storedUser = await TryGetUserFromPersistedCookie();
var claimsPrincipal = IdentityFromUserAccount(storedUser);
return await Task.FromResult(new AuthenticationState(claimsPrincipal));
}
catch
{
//silently fails on prerrendering since JS is not available yet
return await Task.FromResult(new AuthenticationState(_anonymous));
}
}
I am working on a web api and each request is authenticated with Authorize annotation on methods i.e. [Authorize (roles="trader")]
Based on the logs I can tell that multiple requests are entering the Application_PostAcquireRequestState event simultaneously.
As roles are loaded in Application_PostAcquireRequestState event, there can be race condition and some of the calls fail randomly.
I am not sure if I am on right track. The event is application level and Application.Lock() may fix the issue and like to know if it is the correct solution.
I have noticed a couple of calls to the web api failed, which were originated simultaneously.
I appreciate your help.
Global.ascs.cs
protected void Application_PostAcquireRequestState()
{
//Application.Lock();
//get user roles and verify access...
...
//Application.Unlock();
}
controller.cs
[Authorize(Roles = "Trader")]
public async Task<IHttpActionResult> GetOrder(long id)
{
//get order
}
You are wrong, this event, as well as a bunch of others (BeginRequest, AuthenticateRequest, AcquireRequestState etc.) is technically an application-level event (Application_...), however, actually it is a request-level event and multiple copies of the same handler are fired concurrently for different requests.
This means that the sender argument of the handler gives you exact execution context and is intended to be used like
protected void Application_PostAcquireRequestState( object sender, EventArgs e )
{
HttpApplication app = (HttpApplication)sender;
HttpContext ctx = app.Context; // current context
// with the current context in hand you can pretty much access anything
// including the Request, Response and last but not least, User
}
No need for locking or any other means of throttling.
I am only not sure why would you verify the access here, considering the MVC/WebAPI will do it in a moment in the pipeline, based on the Authorize and roles you put there.
I have created an authentication library in wp8 which requires me to provide a login screen UI and return the session id and other login details on authentication with server.. I have an API which returns the login data..
Now in the app im going to the login screen using NavigationService.navigate () which automatically instantiates my library class..
My Question is how do I get the reference to object of class that is instantiated by navigate method.. Also it seems that navigate() is asynchronous as it shows my login page and immediately moves to the line after the call.. My requirement is that I have to call the API that returns login data only after authentication has been performed but I have no reference of the instantiated object to call it
Is there any way to make the navigate() wait until the authentication is complete?
You are getting username and password from login screen and you are using this data for authentication.then,you can call your API in login screen's "say Login" button and after that you can use event delegate mechanism to receive web service response status, if its correct then you can navigate to next screen,till then you can use ProgressBar.
please check this code to get understanding of await:
Object response = await Authenticate(UsernameTextBox.Text, PasswdTextBox.Password);
if (response is success)//typecast your object as per your need to get status of result
{
Navigate
}
else
{
Show error dialog
}
Also dont forget to modify your button event handler's signature with "async".
REWRITTEN QUESTION
I have an ASP.NET MVC 4 site that uses forms auth.
It also needs to retrieve custom user object from a service call and then set it to the HttpCurrent.User.Context.
this works fine but I realised that when it hits the post authenticate request that it will hit it several times per request - not good.
Global.asax.cs:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
IIdentity ui = HttpContext.Current.User.Identity;
MyMembershipUser myUser = new MyMembershipUser (ui.Name);
MyCustomPrincipal myPrincipal = new MyCustomPrincipal (ui, myUser);
HttpContext.Current.User = myPrincipal;
}
}
I cant entirely cache the user for a number of reasons so lets not go there.
so this gets executed a few times per request. This means for every hit, it calls the DB.
Some views on the site use the custom principal to display some user specific details only if they are authenticated. if they aren't, then it wont display it. But if they are authenticated, it gets the principal and casts it to "MyCustomPrincipal" so I can grab the properties I need to display.
How can I prevent these multiple hits?
I tried creating a custom Authorize attribute and doing the above code in there, it works but fails when it renders the view which can see the user is authenticated but fails to do the cast because at that point, the User Identity/principal is still set to the Generic principal.
typical code in the view:
#if (Helpers.UserContext.IsAuthenticated)
{
#: tmpStatus = '#Helpers.UserContext.User.Status';
}
UserContext.IsAuthenticated just returns HttpContext.Current.User.Identity.IsAuthenticated
User in UserContext does the casting:
return HttpContext.Current.User as MyCustomPrincipal
I hope this clarifies the question more!
I want to avoid multiple hits happening on the PostAuthenticateRequest but not sure why those hits are happening. I am not even sure if it is the right place to place it. I want to make sure that the Context User is all setup for subsequent accesses/requests to it without having to call the service layer to get the details again.
thanks
you minimise some action by check if authenticated
//assuming something like....
public override void Init() {
base.Init();
// handlers managed by ASP.Net during Forms authentication
PostAuthorizeRequest += new EventHandler(PostAuthHandler);
}
// try screen out some calls that arent authenticated yet.
public void PostAuthHandler(object sender, EventArgs e) {
if (Request.IsAuthenticated) {
//.... try a break to see how often
}
}
EDIT: But careful of multiple hits due to script and content bundling / loading.
Check the Request.Url value. Is it changing.
Also Note the thread Id. See Debug.Windows.Threads
The thread may also be changing.
Consider thread safety before you attempt any caching / global singletons etc.
You may wish consider moving some code to a controller Or Base Controller
I'm using an external framework that defines an interface with a single method:
bool Authenticate();
that is to contain the whole process of authentication, including user interaction (WinForms). What I would like to do is something like:
bool Authenticate()
{
bool succeeded = false;
bool userCancelled = false;
while(!succeeded && !userCancelled)
{
var credentials = AskTheUserForCredentials(); // this needs to wait for user input before returning!
if (credentials == null)
userCancelled = true;
else
succeeded = AuthenticateWithAnExternalServer(credentials);
if (!succeeded)
ShowErrorMessage();
}
return succeeded;
}
Now the easy way to implement AskTheUserForCredentials() and ShowErrorMessage() is to use Form.ShowDialog() inside. This is really bad user experience, as the dialog disappears for the actual authentication process and error message appears in a new, have-to-click-to-close dialog.
I'd rather have it all in a single form, that stays visible, disables textboxes/buttons appropriately and displays the error message by itself.
How would you do it in this single, blocking method call?
UPDATE
Best solution so far is to implement a message pump inside AskTheUserForCredentials():
Credentials AskTheUserForCredentials()
{
while(NeitherOkNorCancelPressed())
{
Application.DoEvents();
Thread.Sleep(10); // Standard Sleep(0) results in 100% procesor core usage.
}
return CreateCredentialsFromTextboxesEtc();
}
Now we all know message pumps are far from clean.
Exactly how bad is this solution?
Anything better?
UPDATE 2
The message pump had some pitfalls:
being ugly and not fully cpu-effective
working terribly slow with White UIAutomation
I ended up delegating the whole process to a dialog as ChrisBD (the dialog only closes after ultimate success or failure). This took more time to abstract the authentication away from the GUI with IoC, but eventually is clean and works as intended.
I think that you're almost there.
Display a modal dialog that has the user input controls and have that dialog call the Authenticate method when required.
You can then choose when the dialog should close and indeed where any error message is displayed.
Have a property of the modal dialog class indicate whether authentication was successful or not. When you close the dialog class (which was instantiated by your main application before it opened it modally) the main application will continue to run - you can then check to see if authentication was successful or not, by checking the appropriate dialog class property.
*edited here *
If you're implementing the Authenticate method yourself then it is this function that calls the modal dialog, rather than your main application (as stated earlier). The custom form class can run all of your authentication logic and display your user interaction controls, along with any error messages. Remember to remove the minimize and close buttons and set a class property to indicate success or failure of the authentication process.
The calling framework will wait for the return value of your authentication method.