REST Authentication

Hi,

sorry if this was already asked but I couldnt find anything.

I have a external company calendar which supports REST requests.
Now I want to create a rule in openHAB based on my appointment schedule.
For example, when I activated “Out of house” in my appointment settings on the calendar, I want a rule to be activated.

I need to authenticate myself for every REST request, and I was not sure how openHAB does support this.
Can I simply use cURL within rules?

Is it like:

curl --anyauth --user CASio:password http://calendar.com/rest/v6.0/type/appointment/list/...`

Would that work within a rule file?

Assuming that you use a HTTP Action, e.g. sendHttpGetRequest(String url) then I would expect the following to work:

sendHttpGetRequest("http://<username>:<password>@calendar.com/rest/v6.0/type/appointment/list/...")

which in your case would then become:

sendHttpGetRequest("http://CASio:password@calendar.com/rest/v6.0/type/appointment/list/...")

Thanks.
What if I have another header (in my case the client version) to send within the GetRequest aswell? Would this work?

Unfortunately, it seems that the sendHttpGetRequest() method does not support adding HTTP headers. It can be done in the rules DSL but requires a bit more low level coding.

Check out the rules in this topic:

I figured it out by using the HTTP-Binding.

Hi Tobias,
I would be very interested in your solution, do yo mind sharing it with us?

As I dealt with it last sunday, I can give you my solution:

  • unfortunately the HTTP Action doesn’t provide the functionality to add headers to the request - I needed do to this in an http curl
  • therefore I simply wrote a small script with the respective headers, because linux is easily capable to do this without any additional packages
  • so in an openhab rule I execute the scripts, which makes the request. Within the rule the response gets safed in a variable and then I process everything on this variable

This is pretty easy and has the benefit, that you find a lot of infos about get, post, put etc on the internet :slight_smile:

1 Like

Hi,

as NoneWhereTo already mentioned, the HTTP binding isnt good enough for my purposes.

I created a curl string with different variables and used that to get my request:

var String Info = YourHTTPRequest

var String URL = YourHTTPUrl

val String curlcommand = "curl@@-d@@" + Info + "@@POST@@" + URL +"@@-H@@Content-Type:application/json@@-H@@accept:application/json@@-s@@-H@@authorization:Basic XXX  // String from url, body and header

var String json = executeCommandLine(curlcommand, 1000) //Exectues the curl-string

I used @@ instead of quotes and spaces within the String to escape double quotes.
I hope this helps you, If you have any questions feel free to ask.

1 Like

CASio & NoneWhere To: Thanks a lot!

This is a bit embarrassing, but I can’t get curl to deliver an acceptable authentication to the server. I’ve read curl’s man pages and searched various places on the net and I can’t find out what I’m doing wrong.
My plan was to first verify the call from a standard terminal in Linux and then port the solution to a rule in openHAB. Unfortunately, I find myself stuck at step one…

I know that the service I call works as when I test the following in the browser I get a correct JSON response:

http://username:password@192.168.39.21/api/v1/production/inverters/

When I use curl to do the same thing I get an “Authentication required” error from the same server:

curl --user username:password -H Content-Type:application/json -H accept:application/json -H authorization:Basic --trace-ascii curlTest.log --no-keepalive 192.168.39.21/api/v1/production/inverters/

And the log file is:

== Info: Trying 192.168.39.21…
== Info: TCP_NODELAY set
== Info: Connected to 192.168.39.21 (192.168.39.21) port 80 (#0)
=> Send header, 170 bytes (0xaa)
0000: GET /api/v1/production/inverters/ HTTP/1.1
002c: Host: 192.168.39.21
0042: User-Agent: curl/7.52.1
005b: Content-Type:application/json
007a: accept:application/json
0093: Authorization:Basic
00a8:
<= Recv header, 27 bytes (0x1b)
0000: HTTP/1.1 401 Unauthorized
<= Recv header, 37 bytes (0x25)
0000: Date: Fri, 26 Jul 2019 20:48:53 GMT
<= Recv header, 24 bytes (0x18)
0000: WWW-Authenticate: …A
<= Recv header, 32 bytes (0x20)
0000: Content-Type: application/json
<= Recv header, 20 bytes (0x14)
0000: Content-Length: 90
<= Recv header, 2 bytes (0x2)
0000:
<= Recv data, 90 bytes (0x5a)
0000: {. “status”: 401,. “error”: “”,. “info”: “Authentication requ
0040: ired”,. “moreInfo”: “”.}.
== Info: Curl_http_done: called premature == 0
== Info: Connection #0 to host 192.168.39.21 left intact

I know I’m far away from an openHab problem here but I would really appreciate someone elses eyes on this problem.
Tia /Brus-Per

What happens if you remove that header? When using the --user parameter, then basic authentication is the default:

(source)

I added that in a later iteration of my tests. Unfortunately, removing it has no effect on outcome; still the same authentication error.

Finally, I found the solution. It turns out that the authentication scheme accepted by the server (a builtin server I can not configure) was not Basic but rather Digest! Added the --anyauth solved the problem. Posting it here in case it is helpful to others:

curl --user username:password --anyauth -H Content-Type:application/json -H accept:application/json --trace-ascii curlTest.log “your URL, in my case http://192.168.39.21/api/v1/production/inverters/

The resulting output is:

== Info: Trying 192.168.39.21…
== Info: TCP_NODELAY set
== Info: Connected to 192.168.39.21 (192.168.39.21) port 80 (#0)
=> Send header, 149 bytes (0x95)
0000: GET /api/v1/production/inverters/ HTTP/1.1
002c: Host: 192.168.39.21
0042: User-Agent: curl/7.52.1
005b: Content-Type:application/json
007a: accept:application/json
0093:
<= Recv header, 27 bytes (0x1b)
0000: HTTP/1.1 401 Unauthorized
<= Recv header, 37 bytes (0x25)
0000: Date: Sat, 27 Jul 2019 00:09:47 GMT
<= Recv header, 84 bytes (0x54)
0000: WWW-Authenticate: Digest qop=“auth”, realm=“enphaseenergy.com”,
0040: nonce=“1564186186”
<= Recv header, 32 bytes (0x20)
0000: Content-Type: application/json
<= Recv header, 20 bytes (0x14)
0000: Content-Length: 90
<= Recv header, 2 bytes (0x2)
0000:
== Info: Ignoring the response-body
<= Recv data, 90 bytes (0x5a)
0000: {. “status”: 401,. “error”: “”,. “info”: “Authentication requ
0040: ired”,. “moreInfo”: “”.}.
== Info: Curl_http_done: called premature == 0
== Info: Connection #0 to host 192.168.39.21 left intact
== Info: Issue another request to this URL: ‘http://192.168.39.21/api/v1/production/inverters/
== Info: Found bundle for host 192.168.39.21: 0xc69568 [can pipeline]
== Info: Connection 0 seems to be dead!
== Info: Closing connection 0
== Info: Hostname 192.168.39.21 was found in DNS cache
== Info: Trying 192.168.39.21…
== Info: TCP_NODELAY set
== Info: Connected to 192.168.39.21 (192.168.39.21) port 80 (#1)
== Info: Server auth using Digest with user ‘username’
=> Send header, 400 bytes (0x190)
0000: GET /api/v1/production/inverters/ HTTP/1.1
002c: Host: 192.168.39.21
0042: Authorization: Digest username=“username”, realm=“enphaseenergy
0082: .com”, nonce=“1564186186”, uri="/api/v1/production/inverters/",
00c2: cnonce=“N2VjYTE3NGM5NTE3NJ78c504f27e238ff9287bcc374db9609ecd”
013d: User-Agent: curl/7.52.1
0156: Content-Type:application/json
0175: accept:application/json
018e:
<= Recv header, 17 bytes (0x11)
0000: HTTP/1.1 200 OK
<= Recv header, 37 bytes (0x25)
0000: Date: Sat, 27 Jul 2019 00:09:47 GMT
<= Recv header, 32 bytes (0x20)
0000: Content-Type: application/json
<= Recv header, 22 bytes (0x16)
0000: Content-Length: 2757
<= Recv header, 2 bytes (0x2)
0000:
<= Recv data, 2757 bytes (0xac5)
0000: [. {. “serialNumber”: “121412031054”,. “lastReportDate”:
0040: 1564169396,. “lastReportWatts”: 1,. “maxReportWatts”: 193.
0080: },. {. “serialNumber”: “121412031017”,. "lastReportDate
00c0: ": 1564170304,. “lastReportWatts”: 0,. “maxReportWatts”: 1
0100: 91. },. {. “serialNumber”: “121412030724”,. “lastReportD
0140: ate”: 1564169393,. “lastReportWatts”: 0,. “maxReportWatts”
0180: : 195. },. {. “serialNumber”: “121412031058”,. “lastRepo
01c0: rtDate”: 1564169397,. “lastReportWatts”: 1,. "maxReportWat

…and a bunch of JSON data…

0a40: . {. “serialNumber”: “121412035391”,. “lastReportDate”: 1
0a80: 564164879,. “lastReportWatts”: 5,. “maxReportWatts”: 177.
0ac0: }.].
== Info: Curl_http_done: called premature == 0
== Info: Connection #1 to host 192.168.39.21 left intact

Note that the --trace-ascii curlTest.log option is not needed. Interestingly, the --anyauth option was mentioned by CASio in the original question but was lost in the discussion that followed.

Just to add the context in case someone is searching for a solution for this specific problem, this is for polling my solar array with an Enphase/Envoy central unit for production data. The end goal is to create an alternative to the existing Envoy binding ([New Binding] Enphase Envoy Solar System gateway) that unfortunately has a username and password combination hardcoded.

Best /Brus-Per