RaKer
RaKer

Reputation: 289

Accessing a secure Web API from a WPF client

I have a Web API project which is configured for CORS and implements [Authorize] on one of the APIControllers. I can access the API from the WPF client when I remove the [Authorize] but when it's in place the call seems to get lost. This is what I have setup to request a token.

In the WPF code behind

    internal void RefreshRecentFilesList()
    {
        //Get the token
        var token = GetAPIToken(email, password, "http://localhost:50006").Result;
        MessageBox.Show(token);
    }

    private static async Task<string> GetAPIToken(string userName, string password, string apiBaseUri)
    {
        using (var client = new HttpClient())
        {
            //setup client
            client.BaseAddress = new Uri(apiBaseUri);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            //client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.AccessToken);

            //setup login data
            var formContent = new FormUrlEncodedContent(new[]
             {
             new KeyValuePair<string, string>("grant_type", "password"), 
             new KeyValuePair<string, string>("username", userName), 
             new KeyValuePair<string, string>("password", password), 
             });

            //send request
            HttpResponseMessage responseMessage = await client.PostAsync("/Token", formContent);

            //get access token from response body
            var responseJson = await responseMessage.Content.ReadAsStringAsync();
            var jObject = JObject.Parse(responseJson);
            return jObject.GetValue("access_token").ToString();
        }
    }

In the Web API project WebAPIConfig.cs

        //Enable CORS
        var cors = new EnableCorsAttribute("*", "*", "GET");
        config.EnableCors(cors);

In the API controller

    [HttpGet]
    //[Authorize]
    public List<FileInfo> GetFileTypes()
    {
        List<FileInfo> fileInfos = new List<FileInfo>();
        ...

In the StartAuth.cs

public void ConfigureAuth(IAppBuilder app)
    {
        // Configure the db context and user manager to use a single instance per request
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

        // Enable the application to use a cookie to store information for the signed in user
        // and to use a cookie to temporarily store information about a user logging in with a third party login provider
        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            // In production mode set AllowInsecureHttp = false
            AllowInsecureHttp = true
        };

        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);

When I run the WPF and step through the code it's getting to the send request line and then just hangs.

Can anyone point me in the right direction please?

Many thanks

Upvotes: 4

Views: 5557

Answers (2)

Mehdi Benkirane
Mehdi Benkirane

Reputation: 445

complementary of Federico Dipuma solutions is to call Form_Load from constructor using

this.Loaded += new RoutedEventHandler(Form_Load);

Upvotes: 0

Federico Dipuma
Federico Dipuma

Reputation: 18265

I think the primary candidate for your issue is this line:

var token = GetAPIToken(email, password, "http://localhost:50006").Result;

You are calling an async method and then you are waiting for its result synchronously: this will lead to deadlocks. Never do that.

Instead, turn your method (and you entire call stack) asynchronous. If you are inside WPF you are almost certainly calling this method from some kind of event handler callback. Those callbacks supports async (even if they have to return void).

Change your code to something similar to this and then try again (assuming Form_Load is an event handler callback):

private async void Form_Load(object sender, EventArgs e)
{
    await RefreshRecentFilesList();
}

internal async Task RefreshRecentFilesList()
{
    //Get the token
    var token = await GetAPIToken(email, password, "http://localhost:50006");
    MessageBox.Show(token);
}

References:
Why does this async action hang?
How to call asynchronous method from synchronous method in C#?

Upvotes: 4

Related Questions