WebAuthenticationBroker.AuthenticateAsync not working - c#

I am building a Windows 8 application and I'm trying to get a token from the Google data API when the application is starting. I built a function to do this, and it has the following code:
string authCodeUrl = UrlHelpers.BaseUrlFactory(UrlType.OAuth) +
"?client_id=" + _clientId +
"&redirect_uri=" + _redirectUri +
"&response_type=code" +
"&scope=" + _scope;
Uri startUri = new Uri(authCodeUrl);
Uri endUri = new Uri("https://accounts.google.com/o/oauth2/approval?");
WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, startUri, endUri);
I'm calling the function in my App.xaml.cs OnLaunched(), before the Window.Current.Activate() call. The reason I am doing this is because I already need the token in my MainViewModel.
The strange thing is: when I launch my application the normal way (via Visual Studio) it gets stuck at the splashscreen (the splashscreen stays on for ages), but when I place a breakpoint on this line:
WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, startUri, endUri);
and step through it, I suddenly get a log in window that allows me to log in and obtain a token, meaning that the splashscreen goes away and I can use my application.
When I remove the call from my App.xaml.cs and just request the token from my ViewModel, I have the same problem: it's still stuck on the SplashScreen. I also have this problem when I do request a token from my App.xaml.cs but move the request after the Window.Current.Activate() call. But in these cases, the splashscreen goes away after logging in, but the screen stays black. I don't see my app.
P.s., this is how I request the token from my App.xaml.cs (OnLaunched is marked as async):
IOAuth2Service oAuth2Service = new OAuth2Service();
await oAuth2Service.GetToken();
OAuth2Service is just an object that has a method GetToken(). This method just does what I described above.
Does anyone know why it works when I step through the app with a breakpoint, but not when I just launch it without stepping through it?
I've isolated the problem and created a Github project that contains just this code. You can find it here: https://github.com/Avalaxy/OAuth2WinRT/tree/master/App1. There is an app.xaml.cs which calls OAuth2.GetToken().

Per the documentation -
If the app or its splash screen is kept on screen there is no time limit, but eventually the app needs to call Activate to progress.
Admittedly a bit vague and subject to interpretation, but moving the call to activate prior to the GetToken request will get you past what seems like a potential race condition.

Related

C# WinForms Application exits unexpectedly with no exception, but only when the API piece is not on the same machine

I am developing an application which is to run as a WinForms thick-client, accessing both an API to be running in the cloud (Azure), and a local SQL Server DB for data.
To allow users to log in, the login screen is triggered as a Modal prompt when the application starts up with the following code in the HomeScreen form which is the 'main' page of the application:
using (Form loginScreen = new LoginForm())
{
loginScreen.ShowDialog(this);
}
Once the login screen has been passed, the user can see the home screen, if they cancel it, the application closes. Once they get to the home screen, another API call is run to retrieve data about the user from the API for display on the home screen.
All API calls execute the same code, which is below (this is very early code for a 'working prototype' and I am aware there are probably issues with it that require a refactor, at this point I'm really only interested in understanding what is causing my call to PostAsJsonAsync to fail:
public async Task<ApiResponse> sendApiRequest(RequestDetail reqDet)
{
//create a variable to track if the action was done or we need to retry after a timeout and login
bool actionDone = false;
//instantiate a variable for the ApiResponse so it can be used later outside of the scope of the actionDone loop
ApiResponse res = null;
while (actionDone == false)
{
//populate the main SessionKey of the packet from the GlobalData var (for initial dev, to be refactored out)
reqDet.SessionKey = GlobalData.SessionKey;
//populate the SessionKey in the array underneath the main object (for future use)
reqDet.strParameters["SessionKey"] = GlobalData.SessionKey;
//instantiate a new ApiRequest object to hold the main request body
ApiRequest req = new ApiRequest("ClientRequest", reqDet);
//Create HttpClient class for communication with the server
HttpClient client = new HttpClient();
//Set URL and Headers (URL will be in a config file in future
client.BaseAddress = new Uri("https://removed.the.url.for.se/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
//actually call the service, wait for the response, and read it out into the response object
HttpResponseMessage response = await client.PostAsJsonAsync((string)req.requestBody.ApiLocation, req);
res = await response.Content.ReadAsAsync<ApiResponse>();
//check if the response was successful or we need to show an error
if (res.responseType == "Success")
{
//set action done to TRUE so we exit the loop
actionDone = true;
}
else
{
//Use the MessageService to dispaly the error
Error err = res.responseError;
MessagesService ms = new MessagesService();
await ms.displayErrorPrompt(err);
//trigger a login screen and restart the service call if the user's session has expired
if (err.ErrorText.Equals("Session has expired, please log in again"))
{
using (Form login = new LoginForm())
{
login.ShowDialog();
} // Dispose form
}
else
{
// set ActionDone to True if it's not a login error so we don't endlessly call the service
actionDone = true;
}
}
}
//return the final result
return res;
}
When running the entire stack locally, this all works perfectly, I can login and traverse the rest of my application as normal. When running the client locally in VS and the API in Azure, the first call to the Login API succeeds (I can call it multiple times e.g. with a wrong password and it behaves as normal), however the second call to get the user's data to paint on the home screen fails.If I put a breakpoint on the PostAsJsonAsync line, I can see that the line executes once and continues as normal, but immediately after stepping over the line the second time for the user details call, the entire application exits without executing the subsequent code.
What is strange about this is that it exits with a 0x0 return code, does not throw an exception, or in any way behave abnormally other than shutting down after just that line.
I have tried manually calling the APIs on the Azure service in Postman and they all return exactly the same (correct) results I get when running it locally, so I know it is not the deployment to the App Service that is the issue.
Things I have tried to fix it after Googling, reading other SE posts and looking at comments on this question
I have tried enabling first-chance exceptions in Visual Studio for all CLR exceptions. Nothing is caught or thrown that I can see.
Here is a screenshot of my settings in case I've done something wrong
I have tried wrapping just that line in a try-catch block that catches all exceptions. It still immediately stops executing after the PostAsJsonAsync and never reaches the Catch block
Adding the following code to my Program.cs file to catch unhandled exceptions (is never run when I put a breakpoint on it and nothing is written to the console that I can see):
static void Main()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new HomeScreen());
}
static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception)args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
}
Setting a DumpFolder that is writable by all users, and a DumpType of 2 in a key named after my executable at Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\ - I've tried both keys named MyApplication and MyApplication.exe and neither results in a file being produced when the app crashes.
The Windows Event Viewer after the 'crash' (nothing from my application)
Reviewing the request/response in Fiddler - the first 'login' request and response is shown correctly, but the second is not shown at all, so it looks like it's crashing before even sending the request
I'd be extremely grateful for any suggestions you can provide, even if it is only a workaround or 'patch' to resolve the issue. It's extremely strange to me both that it exits the program with no exception and without running the subsequent code, that it only does this when the API piece is running in Azure, not when running locally, and finally that it's only when it gets to the subsequent request after the login.
Update
I have tried commenting out the line that runs the RefreshScreen() function to call the web service again and the application still exits in the same way after the login, but just without hitting my breakpoint a second time. However again only when the application is running against the Azure API and not locally. If I break at the last line of the HomeScreen constructor and keep stepping, it goes back to my Main() method and ends the application. Is there something I'm doing wrong here?
I think the PostAsJsonAsync may have been a red herring so have taken it out of the title.
public HomeScreen()
{
InitializeComponent();
if(GlobalData.SessionKey == null)
{
using (Form loginScreen = new LoginForm())
{
loginScreen.ShowDialog(this);
}
// Dispose form
}
refreshScreen();
}
public async Task refreshScreen()
{
ApiService srv = new ApiService();
ApiResponse res = await srv.sendApiRequest(new Sessions_GetUserDetailsRequest());
if (res.responseType == "Success")
{
foreach (dynamic usrItem in JsonConvert.DeserializeObject(res.responseContent))
{
lblUserName.Text = usrItem.UserGivenName + " " + usrItem.UserSurname;
lblSiteName.Text = usrItem.TenantName;
}
}
}
So after doing some research to answer the helpful comments on this question, I stumbled across the answer.
I have an event in the application that is designed to close the entire application if the user exits the login page without logging in, since otherwise it would return to the 'home screen' form in an invalid state. It contained the following code, designed to close the application if the user didn't have a token (i.e. had cancelled the page):
Because my login process is asynchronous (code above) when I was stepping through the process in VS, I was getting to the "PostAsJsonAsync" step, and it was closing the application without showing me it was running the 'on close' event. However, unknown to me when testing locally, the code had a race condition where it would jump ahead to the 'close form' bit while still awaiting the web service call, and therefore execute the following code:
private void DoOnFormClosing(object sender, FormClosingEventArgs e)
{
if(GlobalData.SessionKey == null || GlobalData.SessionExpiry <= DateTime.Now)
{
Application.Exit();
}
}
The solution was to remove this event as part of the login process, after the login had been validated, meaning this code would never be called if the user had successfully logged in.

How to check if Windows Universal 10 app is closed not minimised?

Hi guys I am aware that OnSuspending function in the app.xaml file allows me to save information should my app be suspended. I want my user to be automatically logged out in my database should the application be closed by the user. Here is my code:
private async void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
if (CommonVariables.LoggedIn)
{
CommonVariables.LoggedIn = false;
string jsonPayload = "{\"user_id\":\"" + CommonVariables.AuthenticateUserResponseDetails.user.id + "\"}";
HttpClient client = new HttpClient();
string postUrl = CommonVariables.SERVER + CommonVariables.LogOut;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, postUrl);
//encrypt tase
EDecrypt encrypt = new EDecrypt();
jsonPayload = encrypt.AES_Encrypt(jsonPayload, CommonVariables.EncryptionKey);
request.Content = new StreamContent(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(jsonPayload)));
var result = await client.SendAsync(request);
string response = string.Empty;
}
//TODO: Save application state and stop any background activity
deferral.Complete();
}
This works for me well so when my user closes the app in their device the function is called and they are logged out. However my issue the function also logs the user out when they minimise the app or put the app to the background and go into another app. So how can I adjust my code so that my logout function only works when the user shuts down the app in their device?
When you minimize an app, there are actually 2 things happening:
Visibility change
App suspension
When the user switches from your app to another app, your app is no
longer visible but remains in the Running state until Windows suspends
it. If the user switches away from your app but activates or switches
back to it before it can suspended, the app remains in the Running
state.
So if a user minimizes/switches to another app and comes back fast enough, your app will keep running and your OnSuspending handler will never execute. While being in the background, the OS will determine when your app goes to suspension and when/if it is terminated (like Hans mentioned in his comment).
So to fix your problem: keep your OnSuspending handler, and handle the app resume event to log back in. This will cover both minimizing and closing.

Windows Phone 8 HttpClient Get method returns strange results

I am developing a Windows Phone 8 app that sends some data to a server which executes it and returns a result. The server can be queried at any time to GET the status of the current execution which could be initializing,running or finished. The output is available only when the execution is in the finished state. The user has the option to check the current status of the execution, by pressing an 'update' button
XAML
<Button Background="{StaticResource PhoneAccentBrush}"
Click="UpdateRunInfo" > Update info</Button>
This is the method
private async void UpdateRunInfo(object sender, RoutedEventArgs e)
{
ExecutionItem clicked = ((sender as Button).DataContext as ExecutionItem);
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential("username", "password");
HttpClient client = new HttpClient(handler);
string Url = "http://somefakeurl.com/server/run/id/status";
string _status = await client.GetStringAsync(Url);
clicked.status = _status;
}
So the problem is that this method work properly only the first time it is called. After that, GetStringAsync() returns the same results as the first call regardless of the actual status of the server.
I have tried this method in a separate Windows Phone project, the result is the same.
To be sure that the server is running correctly I tried again the same C# code this time on a desktop application and it works perfectly fine.
My theory is that because I send the same request multiple times the WP OS is caching the last result and it gives it back to me instead of actually making the GET request to the server.
Why does the HttpClient return a cached status instead of actually getting the status from the server ?
As suggested by the people commenting it was a caching problem.
The suggestion from user L.B. to set client.DefaultRequestHeaders.IfModifiedSince resolved the problem
As already commented, it's a caching problem (simply speaking: your request is cached, so you get the cached response).
Alexei's answer is probably the most used, especially using the current time as query parameter.
Note: guys, please answer, not comment, otherwise the question will remain in "unanswered" status.

Completing Oauth without the use of threading

I am not exactly sure how to explain this so I'll give it my best shot.
Basically I have an application that connects to DropBox. One of the situations I have run into is with the registering process. At the moment during the registration process it connects to DropBox using the default browser, which it then requires the user to login and click allow app to use the service. In return you get a token which the app can use to connect to the service with. The problem I am having is getting the application to wait until the above process is completed. The only way I have figured out to get it to wait is to use system.threading(int). however if the person takes longer then the timeout then it fails to register properly.
I am hoping someone may be able to point me in the right direction and get it to wait without the threading function. I was hoping I could use a if loop or something but i have no idea how to do that properly.
here is the actual Oauth code:
private static OAuthToken GetAccessToken()
{
string consumerKey = "*****";
string consumerSecret = "****";
var oauth = new OAuth();
var requestToken = oauth.GetRequestToken(new Uri(DropboxRestApi.BaseUri), consumerKey, consumerSecret);
var authorizeUri = oauth.GetAuthorizeUri(new Uri(DropboxRestApi.AuthorizeBaseUri), requestToken);
Process.Start(authorizeUri.AbsoluteUri);
return oauth.GetAccessToken(new Uri(DropboxRestApi.BaseUri), consumerKey, consumerSecret, requestToken);
}
and here is the complete oauth function that is called when the registration button is clicked:
var accesstoken = GetAccessToken();
You need to make the Async (asynchronous) version of their GetAccessToken call. One that will call some function of yours when it is complete.
You could also loop until the information is ready, e.g.
while (dataIsNotReady()) {
Thread.Sleep(1000); // sleep for a bit. this is bad, locks up entire thread maybe even application while it sleeps. Make it shorter for less impact.
// TODO add a "timeout", i.e. only try this for X amount of time before breaking out
}
// Now we data is ready let's go
Update:
Perhaps you are better off using a library that can do it async for you e.g. this Dropbox C# library: https://github.com/dkarzon/DropNet

Windows Phone BackgroundAudioPlayer: Loading tracks that have a 301 redirect on the source

I've created an audio player for windows phone, which uses a playlist of songs collected from an external API.
This playlist is saved to both the client app AND the backgroundaudioplayer so that it can continue to play the next track after a song ends even if the app is not running.
However, this API returns source tracks with urls that are actual REDIRECTS to the file to be streamed.
If I attempt to load these directly into the background audio player, I get an exception.
I attempted to workaround this by allowing the client app to do the redirect and capturing the resulting url before passing it to the player, and updating the playlists on both the client and the app.
This works pretty well.
However, if I then navigate away from my app, the background audio agent continues to play, meaning that the mechanism which updates the urls on the client doesn't fire, and the original redirecting urls are loading, crashing the app.
I've attempted to do the same thing on the background audio agent (issuing a HEAD request and getting the resulting uri from the response) but I cannot get this to work; it never returns from the webrequest!
this is the gist of what I'm trying to accomplish:
try
{
var req = WebRequest.Create(src) as HttpWebRequest;
req.Method = "HEAD";
req.BeginGetResponse(ar =>
{
try
{
var response = req.EndGetResponse(ar);
var uri = response.ResponseUri;
selectedTrack.BeginEdit();
selectedTrack.Source = uri;
selectedTrack.EndEdit();
player.Track = selectedTrack;
player.Play();
}
catch (Exception ex)
{
}
}, req);
}
catch (Exception ex)
{
}
but the callback inside never executes, and the program just halts until I kill it.
I'm sure there must be a better way to handle this, but I have found zero documentation about this issue. how in the heck does everyone else handle this?
I've though about firing a timer on the client to just parse through the list and issue requests and update the whole thing on the fly, but this means 100 requests would execute in sequence every time the app launches (since it gets new tracks every time), so this doesn't really seem like a valid solution...
does anyone have any ideas here? they would be much appreciated!

Categories

Resources