Optoscript httpget with user:password (PAC R1/PacControl)

Hi,

Long time, no posts and the ‘General’ Category seems to have gone, so move this wherever you think it best!

Retired now and using a PAC R1 with Node-RED for a home control system. I know I could use MQTT through Node-RED, but, as this is the main controller, I want the PAC R1 to control, and not rely on my Node-RED/Mosquitto install. This means less things to rely on in case of a crash - and I know the PAC R1 to be very reliable with uptimes of, well, I never get that far, but lots of months at least!!

My problem:
I am trying to carry out an Optoscript httpget to retrieve data from a Shelly control module API. The version without using user:password in the call works fine, but as soon as I enable authentication, I add the user:password@ into the httpget command but it fails with -450.

Basically, I am unsure how to add the user:password into the httpget command and was wondering if you can see a way of doing this.

TIA
Colin J

Just to add: the call works OK from a Browser (Firefox, Chromium)

sHttpURLShellyLivRoom = "XX.XXX.XXX.XXX";

sHttpURLShellyLivRoom = "user:password@XXX.XXX.XXX.XXX";
sHttpCmd   = "/settings/color/0";
nHttpPort = 80;
nHttpStatusESP = 99;


nHttpStatusShelly = HttpGet(
  stHttpBody,           // Return body data dest string table
  stHttpHeader,         // Return header data dest string table
  stHttp_SrcStrTblBody, // Source body data string table (may be empty)
  0,                    // 0 for non-secure, <>0 for SSL
  sHttpCmd,             // URL
  nHttpStatusSonoff,    // Status returned by HTTP server
  nHttpPort,            // Port to which you want to connect
  sHttpURLShellyLivRoom );        // Address, (numeric or www.google.com, etc)

  nIndex = 0;

  
  sWebBodyTest = stHttpBody[nIndex];

You may need to add an authorization header to your stHttpHeader table.

See Base64 encoding/decoding sample code for a subroutine to perform the needed base64 encoding.

Here is an example from one of my projects:

sLogBuildString = sLogUser + ":" + sLogPassword;

Base64EncodeDecode(sLogBuildString, sLogBuildString, 1, nJunkStatus );

stHttpRequestHeaderLog[0] = "Host: " + sHttpHostNameLog;
stHttpRequestHeaderLog[1] = "User-Agent: Opto22 PAC";
stHttpRequestHeaderLog[2] = "Authorization: Basic " + sLogBuildString;
stHttpRequestHeaderLog[3] = "Cache-Control: no-cache";
1 Like

DOH! The base64 conversion is now running around at the back of my head in relation to something else I did sometime ago!!

Thank you for that, much appreciated. I will now have a play and report back.

Colin J

Hmmmm, been playing with this and also added Wireshark into the mix! Although I have the Httpget working with no Authorization, it refuses to play with Authorization. I copied headers from the standard Httpget call and then went from there, combining them with the Browser call.

I get the correct data back with no Authorization required, and the “Authorization: Basic” header is ignored.

My error has gone from -450 error code to getting a return from the Shelly of ‘401 Unauthorized’ after doing the base64 encoding (thank you @philip) when Authorization is required.

I had a difference between the Opto subroutine base64 encoding and the Browser encoding, ‘K’ vs ‘=’ as the last character, but this seemed to give no CR and CR when decoded in the CLI (Ubuntu 20.04). The Browser encoding gives the ‘=’ version. (FYI OptoControl running in Virtual Box, but that isn’t affecting anything.)

The really frustrating thing is that I cannot see any communication between the PAC R1 and the Shelly in Wireshark (other IP’s with Shelly recorded) which I find very strange, but then I am no Wireshark expert. Even gone to the lengths of wired network connection to the same Network Switch as the Opto is on!!

All variables now only exist in this Routine/Chart, so no ‘crosstalk’ from the time slicing.

Httpget commands/headers etc. from all the searches now floating in front of the eyeballs!

I will keep plugging away, but if you should have any suggestions, please send them this way. They would be very much appreciated!!

TTFN,
Colin J

sUserPasswordbase64 = "base64encodedString";  //from CLI
//************* OR ****************
//sUserPasswordbase64 = "user:pass";
//Base64EncodeDecode(sUserPasswordbase64, sUserPasswordbase64, 1, nJunkStatus );

nHttpHeaderIndex = 0;

stHttpHeaderTest[nHttpHeaderIndex] = "HTTP /1.1";
nHttpHeaderIndex = nHttpHeaderIndex + 1;
stHttpHeaderTest[nHttpHeaderIndex] = "Host: 172.27.123.152";
nHttpHeaderIndex = nHttpHeaderIndex + 1;
//stHttpHeaderTest[nHttpHeaderIndex] = "User-Agent: Opto22 PAC";
//nHttpHeaderIndex = nHttpHeaderIndex + 1;
stHttpHeaderTest[nHttpHeaderIndex] = "Authorization: Basic " + sUserPasswordbase64;
nHttpHeaderIndex = nHttpHeaderIndex + 1;
stHttpHeaderTest[nHttpHeaderIndex] = "Content-Type: text/plain";
nHttpHeaderIndex = nHttpHeaderIndex + 1;
//stHttpHeaderTest[nHttpHeaderIndex] = "Cache-Control: no-cache, no-store, must-revalidate";
//nHttpHeaderIndex = nHttpHeaderIndex + 1;
//stHttpHeaderTest[nHttpHeaderIndex] = "Pragma: no-cache";
//nHttpHeaderIndex = nHttpHeaderIndex + 1;
//stHttpHeaderTest[nHttpHeaderIndex] = "Expires: -1";
//nHttpHeaderIndex = nHttpHeaderIndex + 1;
//stHttpHeaderTest[nHttpHeaderIndex] = "Transfer-Encoding: chunked";
//nHttpHeaderIndex = nHttpHeaderIndex + 1;
//stHttpHeaderTest[nHttpHeaderIndex] = "Connection: close";

nHttpStatusShelly = HttpGet(
  stHttpBodyTest,           // Return body data dest string table
  stHttpHeaderTest,     // Return header data dest string table
  stHttp_SrcStrTblBodyTest, // Source body data string table (may be empty)
  0,                    // 0 for non-secure, <>0 for SSL
  sHttpCmdTest1,             // URL
  nHttpStatusShelly,    // Status returned by HTTP server
  nHttpPort,            // Port to which you want to connect
  sHttpURLShellyLivRoom // Address, (numeric or www.google.com, etc)
  );        

You need to track this issue down. There shouldn’t be a CR. Also you can probably see what the browser is sending by using the developer tools of the browser (F12) and looking at the request and response headers. Also make sure the API is using “Basic” authentication - you can see the required authentication method in the WWW-Authenticate header that the server will send out.

Just to be safe I have tried both versions of the hashed string at each step, neither have worked.

As a response in the HttpHeader table from the server, I am getting

WWW-Authenticate: Basic realm="user"
Connection: close

I would assume the Connection: close - in this case is because of the 401 error?

(Get Connection: close on valid data also!)

Is your encoded string of the format ‘username:password’?

Hashed string starts out as ‘username:password’ - just the colon in between the strings, no other punctuation.

Just changed the username to something unrelated to the device/problem as there was a possibility that it could be the same as the ‘realm’. Changed it to something unrelated and Realm remained as it was (that of the device - Shelly).

Will play again tomorrow.

Thank you for your help again!

Getting to the point where I need some code written, so the Authorization has to go on the back burner for a short while.

I did check what Node-RED sends to the Shelly device and used exactly the same headers in Optoscript, still 402 Unauthorized. Studied RFC 7617 and 7235 RFC, but that hasn’t helped either. (Even used Aladdin:open sesame from the RFC)

I think I need to get some kind of Network Sniffer between the PAC R1 and the Shelly unit to see what is actually being sent back and forth. That looks like it will take some setting up with a basic network hub rather than a network switch.

From Wireshark/Node-RED (which works OK)…

Screenshot from 2021-02-03 11-52-04

I will return to this at sometime and if I get a resolution I will post.

@philip, thank you for your suggestions, as usual, very helpful and much appreciated.

Best wishes,
Colin J