401 error when webhook calls an ASP.Net WebService that uses Forms authentication

Here is update after coming up with a solution
Trying to to save whoever reads this some. The problem had to do with a web site I created which uses ASP.Net forms authentication. You login with a userid/password and that generate a security token/cookie that is then is passed in with each request from the client. Once the user is logged in they are then presented a screen with buttons to click for opening and close the garage door. This web site also hosts a web service. These service methods get called, via webhooks, by the Photon. But when the Photon calls the service method it is doing so as an unauthenticated user and gets a 401 Unauthorized response.

One solution would be to come up with an approach to generate an authentication cookie, pass it to the Photon, and then find a way to have that cookie passed in the particle.Publish.

But this is way too much work. So I came up with a compromise where some of the service methods are non-secured and public exposed the rest are are secured (require an authentication cookie). The non-secured ones are used by the webhooks and they fortunately don’t do anything that is needing to be protected (like opening and closing the door!)


Original Post
Not sure if this is going to make sense but I’ll try.

I’ve created a webservice that I’m wanting to use as the endpoint to collect sensor data from a photon. I could use one of many publicly available services that exist for this purpose but for learning purposes I’m wanting to do it myself. The service lives in a web site that also presents some web pages that provide a front end for a garage door opener application. The web application is ASP.Net a with Forms authentication.

I’m wanting the photon periodically publish sensor data to the service. I’ve tested everything locally and am now wanting to get the same thing working when the app is running on my hosting provider. But the webhook fails with a 401 unauthorized error if it’s running there. I’m using https when the site is hosted.

I’m able to run this service locally (after opening up my web server to the internet) with webhook is pointing to my publicly opened web server WAN address.

Test run where Publish hits my local server.

event: TestPublishReportGargeStatus
data: {"data":"{\"op\":\"Monitoring\", \"all\":[{\"t\":\"2017-01-15 20:29:37\",\"o\":\"1\",\"c\":\"0\",\"v\":\"160\",\"h\":\"123\",\"s\":\"53\"}]}","ttl":"60","published_at":"2017-
01-15T20:29:37.405Z","coreid":"999999999999999999999999"}
event: hook-sent/TestPublishReportGargeStatus
data: {"data":"undefined","ttl":"60","published_at":"2017-01-15T20:29:37.449Z","coreid":"particle-internal"}
event: hook-response/TestPublishReportGargeStatus/0
data: {"data":"{\"d\":null}","ttl":"60","published_at":"2017-01-15T20:29:37.499Z","coreid":"particle-internal"}

Webhook pointing to my local IIS

  {
    "id": "587ba742c34666275672b924",
    "event": "TestPublishReportGargeStatus",
    "created_at": "2017-01-15T16:45:54.032Z",
    "mydevices": true,
    "url": "http://[MY WAN ADDRESS]/WebService1.asmx/PublishReportGargeStatus",
    "requestType": "POST",
    "json": {
      "sound": "{{sound}}",
      "vibrateHoriz": "{{vibrateHoriz}}",
      "vibrateVert": "{{vibrateVert}}",
      "doorClosed": "{{doorClosed}}",
      "doorOpen": "{{doorOpen}}"
    },
    "noDefaults": false,
    "rejectUnauthorized": true
  },  

Test run when trying to use the service that is residing at my public hosting site

event: ProdPublishReportGargeStatus
data: {"data":"{\"op\":\"Monitoring\", \"all\":[{\"t\":\"2017-01-15 20:18:55\",\"o\":\"1\",\"c\":\"0\",\"v\":\"152\",\"h\":\"112\",\"s\":\"54\"}]}","ttl":"60","published_at":"2017-
01-15T20:18:56.666Z","coreid":"999999999999999999999999"}
event: hook-sent/ProdPublishReportGargeStatus
data: {"data":"undefined","ttl":"60","published_at":"2017-01-15T20:18:56.700Z","coreid":"particle-internal"}
event: hook-error/ProdPublishReportGargeStatus/0
data: {"data":"error status 401 from www.myinternetsite.com","ttl":"60","published_at":"2017-01-15T20:18:56.918Z","coreid":"particle-internal"}

Webhook for the hitting the public running service

  {
    "id": "587bd6ba7b017726e6f0b486",
    "event": "ProdPublishReportGargeStatus",
    "created_at": "2017-01-15T20:08:26.174Z",
    "mydevices": true,
    "url": "https://www.myinternetsite.com/WebService1.asmx/PublishReportGargeStatus",
    "requestType": "POST",
    "json": {
      "sound": "{{sound}}",
      "vibrateHoriz": "{{vibrateHoriz}}",
      "vibrateVert": "{{vibrateVert}}",
      "doorClosed": "{{doorClosed}}",
      "doorOpen": "{{doorOpen}}"
    },
    "noDefaults": false,
    "rejectUnauthorized": true
  }

I’m thinking some authorization “thing” needs to be passed via the web hook. I updated the webhook by addding the auth token (see webhook documentation). The auth keyword lets you pass a userid and password. But that didn’t work. I also tried “rejectUnauthorized”: false in the webhook definition and that failed as well.

Any ideas?

Thank you.

Does POST-ing to your website require authentication?

I would recommend using https://www.hurl.it/ to test against your hosted website to see if you managed to POST the data successfully. This will help us track down what’s the issue. :smile:

I traced it to the ASP.Net Forms authentication. And I was not having this enabled when running locally so that made it even more difficult to figure out what was gong on. Talk about opaque error messages!

These web.config entries were the cause of the problem. Once removed then the Particle.publish could hit my server. Same behavior was occurring when running locally as when it was hosted on the external hosting site. Of course now I have no authentication. I decided to use ASP.Net Forms because it easy to just turn it on and get authentication needing to know much about it.

<authentication mode="Forms">
  <forms loginUrl="StartPage.aspx" name=".AUTHCOOKIE" />
</authentication>
<authorization>
  <deny users="?" />
</authorization>

Boy did it take allot of iterations to figure this out! Now I gotta figure out how to hopefully configure the ASP.Net forms web config so it will accept the POST from particle.

I watched what the POST coming in from particle looked like using Microsoft Network Monitor. Fiddler did no good as it didn’t see the incoming request.

- Http: Request, POST /WebService1.asmx/PublishReportGargeStatus 
    Command: POST
  - URI: /WebService1.asmx/PublishReportGargeStatus
     Location: /WebService1.asmx/PublishReportGargeStatus 
    ProtocolVersion: HTTP/1.1
    UserAgent:  ParticleBot/1.1 (https://docs.particle.io/webhooks)
    Host:  [MY WAN ADDRESS]
    accept:  application/json
  - ContentType:  application/json
     MediaType:  application/json
    ContentLength:  335
    Connection:  keep-alive
    HeaderEnd: CRLF
  - payload: HttpContentType =  application/json
     HTTPPayloadLine: {"event":"TestPublishReportGargeStatus","data":"{\"op\":\"Monitoring\", \"all\":[{\"t\":\"2017-01-16 18:45:45\",\"o\":\"1\",\"c\":\"0\",\"v\":\"171\",\"h\":\"159\",\"s\":\"51\"}]}","published_at":"2017-01-16T18:45:45.425Z","coreid":"9999999999

- Http: Response, HTTP/1.1, Status: Ok, URL: /WebService1.asmx/PublishReportGargeStatus 
    ProtocolVersion: HTTP/1.1
    StatusCode: 200, Ok
    Reason: OK
    Cache-Control:  private, max-age=0
    ContentLength:  10
  - ContentType:  application/json; charset=utf-8
   - MediaType:  application/json; charset=utf-8
      MainType:  application/json
      charset: utf-8

    Server:  Microsoft-IIS/7.5
    XAspNetVersion:  4.0.30319
    XPoweredBy:  ASP.NET
    Date:  Mon, 16 Jan 2017 18:45:45 GMT
    HeaderEnd: CRLF
  - payload: HttpContentType =  application/json; charset=utf-8
     HTTPPayloadLine: {"d":null}

I also did a crude logging implementation to capture the request when it hit the server.

protected void Application_BeginRequest(object sender, EventArgs e)
{
    var logFile = $"{Server.MapPath("~")}\\logs\\{DateTime.Now.ToString("yyyy.MMdd.HH")}.txt";
    var r = ((HttpApplication)sender).Request;
    var msg =
        $"\n\n{DateTime.Now.ToString("yyyy.MMdd.HHmm.ss.fff")}\nAppRelativeCurrentExecutionFilePath={r.AppRelativeCurrentExecutionFilePath}\nContentType={r.ContentType}\nCurrentExecutionFilePath={r.CurrentExecutionFilePath}\n" +
        $"Headers={r.Headers}\nIsAuthenticated={r.IsAuthenticated}\nIsLocal={r.IsLocal}\nIsSecureConnection={r.IsSecureConnection}\nPath={r.Path}\nPhysicalPath={r.PhysicalPath}\n" +
        $"QueryString={r.QueryString}\nRequestType={r.RequestType}\nUrl={r.Url}\nUserAgent={r.UserAgent}\nUserHostAddress={r.UserHostAddress}\nUserHostName={r.UserHostName}";
    File.AppendAllText(logFile, msg);

Here is some of that output:

2017.0116.1359.15.150

    AppRelativeCurrentExecutionFilePath=~/WebService1.asmx
    ContentType=application/json
    CurrentExecutionFilePath=/WebService1.asmx
    Headers=Connection=keep-alive&
    	Content-Length=335&
    	Content-Type=application/json&
    	Accept=application/json&
    	Host=[MY WAN ADDRESS]&
    	User-Agent=ParticleBot/1.1+(https://docs.particle.io/webhooks)
    IsAuthenticated=False
    IsLocal=False
    IsSecureConnection=False
    Path=/WebService1.asmx/PublishReportGargeStatus
    PhysicalPath=C:\Workspaces\IOTVS\GarageDoor\GarageDoor\WebService1.asmx
    QueryString=
    RequestType=POST
    Url=http://[MY WAN ADDRESS]/WebService1.asmx/PublishReportGargeStatus
    UserAgent=ParticleBot/1.1 (https://docs.particle.io/webhooks)
    UserHostAddress=54.209.186.133
    UserHostName=54.209.186.133

So now I’m hoping I’ve capture everything possible that would help in figuring out whatever security thing needs to be configured to make this work. That is to say to continue ASP.Net Forms authentication. Any ASP.Net authentication experts out here?


Update 1
I did manage to put some authentication in place. But I had to exclude the webmethod calls! BAD BAD BAD. Am thinking I’m trying to force a “legacy” authentication technology to go beyond what it was intended to do. I’m guessing that ASP.Net forms authentication is meant for controlling access to web pages when there is an interactive log in process being done from a screen. And it’s not meant to provide authentication for a public facing API. Things are more complicated because the service is not be called directly but is instead coming in from the proxy call being made by the particle cloud. Not sure where I’m going to end up with this and am hoping the fix is to get away from using old school ASP.Net Webmethods and switch to an MVC approach (which I barely know anything about).


Update 2

This web.config is secures all access except calls that are being made to to methods in WebHookMethods.axmx which contains the methods that get called by the webhooks. These methods are public and are not authenticated. In the grand scheme of things I figure it’s not a big deal as the methods being exposed don’t do anything that is high risk.

<system.web>
    <authentication mode="Forms">
        <forms loginUrl="StartPage.aspx" name=".AUTHCOOKIE" />
    </authentication>
    <authorization>
        <deny users="?" />
    </authorization>
</system.web>
<location path="WebHookMethods.asmx">
    <system.web>
        <authorization>
            <allow users="*" />
        </authorization>
    </system.web>
</location>

There is an additional fix you can put in which will prevent the built in displaying of the details of the web service [Disable the Documentation Protocol for ASP.NET Web Services] 1. This is not a bad idea because it prevents someone from gaining knowledge on how to call a public exposed service.