1

i'm trying to access a WebAPI which is using ValidateAntiForgeryToken. My WebAPI Method is this (a simple one), which is inside a User Controller (just for a test):

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Test(String field)
{
    String result = String.Empty;
    if (ModelState.IsValid)
    {
        HtmlSanitizer sanitizer = new HtmlSanitizer();

        try
        {
            result = sanitizer.Sanitize(field);
        }
        catch (Exception ex)
        {
            result = ex.Message;
            throw;
         }
    }
    return Json(result);
}

With Ajax, i can access it with ease:

$.ajax({
    url: '/User/Test',
     type: "POST",
    contentType: "application/x-www-form-urlencoded",

    data: {

        field: self.textField(),
         __RequestVerificationToken: $("input[name='__RequestVerificationToken']").val(),
    },
    success: function(e) {
       self.textField(e)
        self.divField(e);
    },
    error: function(e) {
        console.log(e.error());
    },
});

But, until now, i can't access this webapi with httpclient on xamarin. This is my code:

    private async void DoTestWebApi()
{
    try
    {
        HttpClient clientPage = new HttpClient()
        {
            BaseAddress = new Uri("https://localhost:44356/user")
        };

        var pageWithToken = await clientPage.GetAsync(clientPage.BaseAddress);

        String verificationToken = GetVerificationToken(await pageWithToken.Content.ReadAsStringAsync());

        HttpClient client = new HttpClient()
        {
            BaseAddress = new Uri("https://localhost:44356/user/test/")
        };

        HttpRequestMessage message = new HttpRequestMessage()
        {
            RequestUri = new Uri("https://localhost:44356/user/test/"),
            Method = HttpMethod.Post
        };

        message.Headers.Add("__RequestVerificationToken", verificationToken);

        String field = "teste";

        //StringContent content = new StringContent("field=test", Encoding.UTF8, "application/x-www-form-urlencoded");
        StringContent content = new StringContent("__RequestVerificationToken=" + verificationToken + ",field=test", Encoding.UTF8, "application/x-www-form-urlencoded");

        // this doesn't work
        //client.DefaultRequestHeaders.Add("__RequestVerificationToken", verificationToken);
        var response2 = await client.SendAsync(message);

        if (response2.IsSuccessStatusCode)
        {
            var t = response2.Content.ReadAsStringAsync();

            if (true)
            {
                // just to check if t has value
            }
        }

    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw;
    }

}

Honestly, i don't know what else i could do to pass my anti forgery token inside the message. It works perfectly in ajax, i pass it inside the data content, but in xamarin it doesn't work. All the code is executed inside the same localhost. If i remove the [ValidateAntiForgeryToken], it works.

What am i missing?

Edit:

Ok, so now i'm sending with cookies, but is not hitting the method again. This is my update:

HttpClient clientPage = new HttpClient()
{
    BaseAddress = new Uri("https://localhost:44356/user")
};

var pageWithToken = await clientPage.GetAsync(clientPage.BaseAddress);

String verificationToken = GetVerificationToken(await pageWithToken.Content.ReadAsStringAsync());

List<KeyValuePair<String, String>> cookiesInfo = new List<KeyValuePair<String, String>>();

foreach (var item in pageWithToken.Headers)
{
    cookiesInfo.Add(new KeyValuePair<String, String>(item.Key, item.Value.ToString()));
}

cookiesInfo.Add(new KeyValuePair<string, string>("field", "value"));
cookiesInfo.Add(new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken));

CookieContainer cookieContainer = new CookieContainer();

using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
{
    using (var client = new HttpClient(handler) { BaseAddress = new Uri("https://localhost:44356/user") })
    {
        var content = new FormUrlEncodedContent(cookiesInfo);

        cookieContainer.Add(client.BaseAddress, new Cookie("__RequestVerificationToken", verificationToken));

        foreach (var item in cookiesInfo)
        {
            cookieContainer.Add(client.BaseAddress, new Cookie(item.Key, item.Value));
        }

        var result = client.PostAsync(new Uri("https://localhost:44356/user/test"), content).Result;

        result.EnsureSuccessStatusCode();
    }

};

This is driving me nuts... Ok the test is in localhost but soon this app will be in Azure, and this is a pre-requisite...


Edit: GetVerificationToken Method:

private string GetVerificationToken(String verificationToken)
    {
        if (verificationToken != null && verificationToken.Length > 0)
        {
            verificationToken = verificationToken.Substring(verificationToken.IndexOf("__RequestVerificationToken"));
            verificationToken = verificationToken.Substring(verificationToken.IndexOf("value=\"") + 7);
            verificationToken = verificationToken.Substring(0, verificationToken.IndexOf("\""));
        }

        return verificationToken;
    }
3
  • Can you capture the requests sent to your API using a tool like Fiddler and compare the ajax one with the HttpClient. This may give you some clues as to what the differences are. Commented Feb 14, 2017 at 20:06
  • localhost? localhost used within your phone/emulator would be the phone/emulator not your PC. Use a Hostname/IP address of the host that is running your aspnet/iis application that is resolvable from the phone/emulator, try using the device's browser to open that url/page as a test... Commented Feb 14, 2017 at 20:08
  • yeah, localhost. because all apps are being executed in the same machine. And it works, that's not the problem (if i remove the ValidateAntiForgeryToken, runs fine) Commented Feb 16, 2017 at 13:48

3 Answers 3

2

ValidateAntiForgeryToken is also expecting a cookie with __RequestVerificationToken and the value provided. This is to make sure that the one posting to the controller is the one who viewed the form.

Sign up to request clarification or add additional context in comments.

1 Comment

I've updated my question, but it seems that the cookie is not only the problem. I'm sending now with cookies. I'm trying with a tool called Advanced Rest Client (Chrome), and even if i set the cookies in this tool, it doesn't work.
2

Thanks to @Zroq tip, i've finally made it. The cookie was indeed missing. This is the final version of my method which sends data to a WebApi with AntiForgeryToken in Asp.NET MVC 5.0:

private async void DoTestWebApi()
    {
        try
        {
            CookieContainer cookieContainer = new CookieContainer();

            HttpClientHandler handlerhttps = new HttpClientHandler
            {
                UseCookies = true,
                UseDefaultCredentials = true,
                CookieContainer = cookieContainer
            };

            HttpClient clientPage = new HttpClient(handlerhttps)
            {
                BaseAddress = new Uri("https://localhost:44356/user")
            };

            var pageWithToken = await clientPage.GetAsync(clientPage.BaseAddress);

            String verificationToken = GetVerificationToken(await pageWithToken.Content.ReadAsStringAsync());

            var cookies = cookieContainer.GetCookies(new Uri("https://localhost:44356/user"));

            using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer, UseDefaultCredentials = true, UseCookies = true })
            {
                using (var client = new HttpClient(handler) { BaseAddress = new Uri("https://localhost:44356/user/test") })
                {
                    var contentToSend = new FormUrlEncodedContent(new[]
                    {
                        new KeyValuePair<string, string>("field", "value"),
                        new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken),
                    });

                    var response = client.PostAsync(client.BaseAddress, contentToSend).Result;
                }
            };
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

Thanks again @Zroq.

10 Comments

Hi.Where is GetVerificationToken() method ?
Is there any way to send object instead of string ? I dont want to use Json.Deserialized on server side . because I want to use ModelState.
i'm not in my PC right now, but AFAIK you can send as StringContent, which you send JSON objects to your server: stackoverflow.com/questions/11145053/… and stackoverflow.com/questions/18971510/… This method expects only a JSON object or String, as far i can remember, you can't send your Custom Object into it.
Yes,I tried that methods but my object are always empty in server side.
how to put verification token to somthing like this : var stringContent = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(url, stringContent);
|
1

For anyone that want the GetVerificationToken() body :

private string GetVerification(string responseBody)
{
     var data = QueryHelpers.ParseQuery(queryString: responseBody);
     string firstValue = data[key: "<input name"];
     var cutedValue = firstValue.Remove(startIndex: 0, count: 50);
     var result = cutedValue.Split('"')[0];
     return result;
}

2 Comments

i've added my GetVerificationToken Method on the question.
Sorry, I didn't see that .

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.