4

I'm currently building an Angular 2 + ASP Web API 2 application and came across an annoying authorization issue.

I can create an account and log in using the standard identity framework without any problem using the bearer token, but when I'm trying to POST data to my controller I have a 401 authorization ... which occurs only on the second call.

Here is for example the first POST which worked perfectly fine:

Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, br
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Authorization:Bearer 2x4FSZqJ6Msos870_gIl4aKjgdms1PEGNnFp2ptM6Rrgs4vtmvnMdu2nzEfBoly15CI2bQss5DVe-bkN2uSTHrMP7F6blK90DcFt095xTsWk3BJ_5RiZ-jsXOrqTZaisChIbWgGN0o-DiTEA_ojFKImgsX9yip6hloZ6GI_Cd0eg6EjX6S_PUmmyI13oiBAHKROmDvVoB4y0-DbHPnAO--x9yGxU1z_SRwFYqX8Dua7oAvpbyl2VFIqqA39DlQ0E9JPaRC0gvrBxeS-nibAaBeDUwLSAQm6HOe-vynVhQeGrBvHl7r3gicaNdaS5UZvrC43KFTe6__wR1aDaIgpMejlp-eVnjTVbcxvR16XOXrud1W-tNcoOHtoMdGKKb0IvfxK_GCety5eiiTIGWUpA26nF5cCZEIna8ZJawXRiBZVV__MEPDBlR68mJHvHVKfm5w_jupwF5_oehcKwbT_QZ92hxg4UV8uUaiisqbIe7jQ
Connection:keep-alive
Content-Length:60
Content-Type:application/json
Cookie:.AspNet.Cookies=wcZzztFwOl41pBDgXshPnYCaaQhULRqu9O6grYPDhUx4cSY8PRY1oBRQLs3gPz4ySoxQBaaehtCLDeOFzXAN7q_UMbZps82aCajwvBQewtu_SLizCRTU9UHncWy0EFnJtLAuF5u_8sKW6sNNPTHfDtmjl3UVQkkvYDBceJC5F-sISGqH-sPFwEGmoXgcKLHWfPlejxAvRCRvGbhFhrdKpk_sycoi_B0sBe9Kc8EULXlybEeUolyyrY7L5HvOUIuujLThILt6ipYEmIk8b_2x32uCq7euh5Y_RDzI009SMceOSBs6HYsqUxz6lR2F3KvlcYBQ3rDr8qALhJBnMyJsysdDIruF9dVjK7-IjdEBPXMsGCnHK5gQs_1bIflSaPBbxVPn2VzPui-WChDCdoVXRgGRRnaEtkzaTOhBZIjlLZ1DWS1MqkM-V0khFQxBDqsxll0pZTgNZuwEOLBYoWWiAK9fATXLSmtMYA2UMKoAE0M
Host:localhost:56762
Origin:http://localhost:56762
Referer:http://localhost:56762/tasks/details;id=null
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

With this data: {Title: "Test", Category: "Test", Entries: []}

And now the one returning a 401 error, performed a few seconds later:

Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, br
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Authorization:Bearer 2x4FSZqJ6Msos870_gIl4aKjgdms1PEGNnFp2ptM6Rrgs4vtmvnMdu2nzEfBoly15CI2bQss5DVe-bkN2uSTHrMP7F6blK90DcFt095xTsWk3BJ_5RiZ-jsXOrqTZaisChIbWgGN0o-DiTEA_ojFKImgsX9yip6hloZ6GI_Cd0eg6EjX6S_PUmmyI13oiBAHKROmDvVoB4y0-DbHPnAO--x9yGxU1z_SRwFYqX8Dua7oAvpbyl2VFIqqA39DlQ0E9JPaRC0gvrBxeS-nibAaBeDUwLSAQm6HOe-vynVhQeGrBvHl7r3gicaNdaS5UZvrC43KFTe6__wR1aDaIgpMejlp-eVnjTVbcxvR16XOXrud1W-tNcoOHtoMdGKKb0IvfxK_GCety5eiiTIGWUpA26nF5cCZEIna8ZJawXRiBZVV__MEPDBlR68mJHvHVKfm5w_jupwF5_oehcKwbT_QZ92hxg4UV8uUaiisqbIe7jQ,Bearer 2x4FSZqJ6Msos870_gIl4aKjgdms1PEGNnFp2ptM6Rrgs4vtmvnMdu2nzEfBoly15CI2bQss5DVe-bkN2uSTHrMP7F6blK90DcFt095xTsWk3BJ_5RiZ-jsXOrqTZaisChIbWgGN0o-DiTEA_ojFKImgsX9yip6hloZ6GI_Cd0eg6EjX6S_PUmmyI13oiBAHKROmDvVoB4y0-DbHPnAO--x9yGxU1z_SRwFYqX8Dua7oAvpbyl2VFIqqA39DlQ0E9JPaRC0gvrBxeS-nibAaBeDUwLSAQm6HOe-vynVhQeGrBvHl7r3gicaNdaS5UZvrC43KFTe6__wR1aDaIgpMejlp-eVnjTVbcxvR16XOXrud1W-tNcoOHtoMdGKKb0IvfxK_GCety5eiiTIGWUpA26nF5cCZEIna8ZJawXRiBZVV__MEPDBlR68mJHvHVKfm5w_jupwF5_oehcKwbT_QZ92hxg4UV8uUaiisqbIe7jQ
Connection:keep-alive
Content-Length:62
Content-Type:application/json
Cookie:.AspNet.Cookies=wcZzztFwOl41pBDgXshPnYCaaQhULRqu9O6grYPDhUx4cSY8PRY1oBRQLs3gPz4ySoxQBaaehtCLDeOFzXAN7q_UMbZps82aCajwvBQewtu_SLizCRTU9UHncWy0EFnJtLAuF5u_8sKW6sNNPTHfDtmjl3UVQkkvYDBceJC5F-sISGqH-sPFwEGmoXgcKLHWfPlejxAvRCRvGbhFhrdKpk_sycoi_B0sBe9Kc8EULXlybEeUolyyrY7L5HvOUIuujLThILt6ipYEmIk8b_2x32uCq7euh5Y_RDzI009SMceOSBs6HYsqUxz6lR2F3KvlcYBQ3rDr8qALhJBnMyJsysdDIruF9dVjK7-IjdEBPXMsGCnHK5gQs_1bIflSaPBbxVPn2VzPui-WChDCdoVXRgGRRnaEtkzaTOhBZIjlLZ1DWS1MqkM-V0khFQxBDqsxll0pZTgNZuwEOLBYoWWiAK9fATXLSmtMYA2UMKoAE0M
Host:localhost:56762
Origin:http://localhost:56762
Referer:http://localhost:56762/tasks/details;id=null
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

With this data: {Title: "Test2", Category: "Test2", Entries: []}

I see no real differences between my two posts. They basically are the same. When I refresh in the browser, re-log, and do these tests again, it behaves the exact same way: 1st request returns HTTP 200, second HTTP 401.

Here is my controller action:

[HttpPost]
[Route("Create")]
[InjectUserIdInServiceFilter]
public int CreateTask(CreateUpdateTimedTaskViewModel timedTask)
{
      var model = this.mapper.Map<CreateUpdateTimedTaskViewModel, TimedTask>(timedTask);
      return this.taskService.Create(model);
}

With my controller definition being the following:

[Authorize]
[RoutePrefix("api/TimedTask")]
public class TimedTaskController : ApiController

Please note that I use the default auto-generated code for the Identity / OAuth / Owin part; I didn't alter its behavior.

Does anybody have any clue about what is happening?

Thanks in advance

T. Thomas

Thomas KiTe Trentin
  • 600
  • 1
  • 5
  • 19
  • most probably your token is expired. can you create a new bearer token and then try again ? – Jawand Singh Mar 07 '17 at 06:01
  • in your class named `Startup.Auth.cs` you can find `OAuthServerOptions` and in that you can find property named `AccessTokenExpireTimeSpan = TimeSpan.FromHours(2)` you can change time here if you want to increase the expiration time of bearer token. Additionally you should implement refresh tokens in your application. – Jawand Singh Mar 07 '17 at 06:05
  • I have this set up: AccessTokenExpireTimeSpan = TimeSpan.FromDays(14). Thus I don't think the issue comes from here. I'll take a look at refresh token, but still I don't get why it doesn't work as it is. – Thomas KiTe Trentin Mar 07 '17 at 13:51
  • have you tried any other API you have written ? there is this default Controller named `ValuesController` try and call the `GET` method and it should return you some value. – Jawand Singh Mar 07 '17 at 15:53
  • It was working, and it was also working for some get methods I've written. I have commented the following: "config.SuppressDefaultHostAuthentication();" and now it works. I'm not really sure why though, since I had "config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));" right after. Do you have any idea about this ? – Thomas KiTe Trentin Mar 07 '17 at 17:32
  • i have no idea why it is behaving like this my application works fine with `config.SuppressDefaultHostAuthentication();` – Jawand Singh Mar 07 '17 at 19:01
  • Okay now this is getting weird: I'm using chrome extension ARC to simulate the post. I retrieve the header / data of a failed post through any web browser, use it in ARC, press send, and it process without any issues. This is kind of maddening – Thomas KiTe Trentin Mar 07 '17 at 23:39
  • @JawandSingh Ok I figured out what my problem was. It was a stupid mistake of mine client side, which I detailed in an answer (I must wait one hour to accept it). Thanks for your time and sorry for the mislead. – Thomas KiTe Trentin Mar 09 '17 at 03:57

1 Answers1

2

Ok so I figured out what was wrong, after about 4-5 hours of investigation.

The issue didn't come from the back end side (web api / MVC or anything), but from the client side.

I had a wrapper around the "http" of angular 2 used to automatically create the headers to use during the call, and the logic here was flawed ending up in a duplication of the "authenticate" header, making it invalid.

I was appending Authenticate: Bearer XXXXXX on the first call, which was working, and corrupting this header on the second call.

If you look closely the "Authenticate" of the second call, the token is repeated two times, thus resulting in an authentication issue.

I only could figure it out while debugging the C# part step by step, allowing me to notice that the Authenticate header was empty in the Request.Headers.

This issue was really tricky to find, because all the error messages were misleading to the wrong direction.

Thomas KiTe Trentin
  • 600
  • 1
  • 5
  • 19