LogicMachine Forum
Digest Auth - JSON - Printable Version

+- LogicMachine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: Digest Auth - JSON (/showthread.php?tid=3096)



Digest Auth - JSON - Krstfr2k - 08.01.2021

Hello!
Is there anyone that can provide me with a script to recieve a JSON table from an API using the Digest Auth?


RE: Digest Auth - JSON - admin - 11.01.2021

See this thread: https://forum.logicmachine.net/showthread.php?tid=1061&pid=9364#pid9364


RE: Digest Auth - JSON - Joep - 15.07.2023

I tried the script from the thread but i got this as a result:

* arg: 1
* string:
* arg: 2
* string: missing credentials in url
* arg: 3
* nil

The credentials are in the url like: https://myemail@gmail.com:password@url
Is it because the username is an email address or is there something else what's wrong? I try to get the data of some JSON string.
When i try the complete url in the browser it does work.

(15.07.2023, 14:44)Joep Wrote: I tried the script from the thread but i got this as a result:

* arg: 1
  * string:
* arg: 2
  * string: missing credentials in url
* arg: 3
  * nil

The credentials are in the url like: https://myemail@gmail.com:password@url
Is it because the username is an email address or is there something else what's wrong? I try to get the data of some JSON string.
When i try the complete url in the browser it does work.

Yes the email address for the user seems to be the problem. When logging log(url.user) i just got the first part of the email address back and not the complete email address. That makes sense as the part after the @ is seen as the url of course. But how to solve this as i can't change the user.


RE: Digest Auth - JSON - Joep - 15.07.2023

Google is your best friend..

I replaced the @ with %40 so the credentials part is solved. But now i get this error: missing realm/nonce from response
I have no idea what it means.. How can i solve this? I'm trying to get the status of my sauna controllers API https://api.huum.eu/action/home/status


RE: Digest Auth - JSON - Joep - 17.07.2023

(15.07.2023, 15:58)Joep Wrote: Google is your best friend..

I replaced the @ with %40 so the credentials part is solved. But now i get this error: missing realm/nonce from response
I have no idea what it means.. How can i solve this? I'm trying to get the status of my sauna controllers API https://api.huum.eu/action/home/status

This is the raw header: 
WWW-Authenticate Basic realm="HUUM api"

Is it a problem due to the capital characters or the whitespace in the header name?
Hope someone can help me out. Below the code so far.

Code:
local skthttp = require('socket.http') local skturl = require('socket.url') local ltn12 = require('ltn12') local md5sum = require('encdec').md5 local hash = function(...)   return md5sum(table.concat({...}, ':')) end local parse_header = function(header)   local result = {}   for key, value in (header .. ','):gmatch('(%w+)=(.-),') do     if value:sub(1, 1) == '"' then -- strip quotes       result[ key:lower() ] = value:sub(2, -2)     else       result[ key:lower() ] = value     end   end   return result end local make_digest_header = function(headers)   local digest = {}   for _, header in ipairs(headers) do     if not header.unquote then       header[ 2 ] = '"' .. header[ 2 ] .. '"'     end     digest[ #digest + 1 ] = header[ 1 ] .. '=' .. header[ 2 ]   end   return 'Digest ' .. table.concat(digest, ', ') end local _request = function(req)   if not req.url then     return nil, 'missing url'   end   local url = skturl.parse(req.url)   local user, password = url.user, url.password   local sink = req.sink   if not user or not password then     return nil, 'missing credentials in url'   end   url.user, url.password, url.authority, url.userinfo = nil, nil, nil, nil   req.url = skturl.build(url)   local source   if req.source then     local chunks = {}     local capture = function(chunk)       if chunk then         chunks[ #chunks + 1 ] = chunk       end       return chunk     end     local chunk_id = 0     source = function()       chunk_id = chunk_id + 1       return chunks[ chunk_id ]     end     req.source = ltn12.source.chain(req.source, capture)   end   req.sink = nil   local body, code, hdrs = skthttp.request(req)   if code == 401 and hdrs['www-authenticate'] then     local ht = parse_header(hdrs['www-authenticate'])     if not ht.realm or not ht.nonce then       return nil, 'missing realm/nonce from response'     end     local qop = ht.qop     if qop and qop ~= 'auth' then       return nil, 'unsupported qop ' .. tostring(qop)     end     if ht.algorithm and ht.algorithm:lower() ~= 'md5' then       return nil, 'unsupported algo ' .. tostring(ht.algorithm)     end     local nc = '00000001'     local cnonce = string.format('%08x', os.time())     local uri = skturl.build({ path = url.path, query = url.query })     local method = req.method or 'GET'     local response = hash(       hash(user, ht.realm, password),       ht.nonce,       nc,       cnonce,       'auth',       hash(method, uri)     )     req.headers = req.headers or {}     local auth = {       { 'username', user },       { 'realm', ht.realm },       { 'nonce', ht.nonce },       { 'uri', uri },       { 'cnonce', cnonce },       { 'nc', nc, unquote = true },       { 'qop', 'auth' },       { 'algorithm', 'MD5' },       { 'response', response },     }     if ht.opaque then       table.insert(auth, { 'opaque', ht.opaque })     end     req.headers.authorization = make_digest_header(auth)     if not req.headers.cookie and hdrs['set-cookie'] then       -- not really correct but enough for httpbin       local cookie = (hdrs['set-cookie'] .. ';'):match('(.-=.-)[;,]')       if cookie then         req.headers.cookie = '$Version: 0; ' .. cookie .. ';'       end     end     if req.source then       req.source = source     end     req.sink = sink     body, code, hdrs = skthttp.request(req)   end   return body, code, hdrs end local request = function(url)   local t = type(url)   if t == 'table' then     return _request(table.clone(url))   elseif t == 'string' then     local req = {}     local _, code, headers = _request({ url = url, sink = ltn12.sink.table(req) })     return table.concat(req), code, headers   end end url = 'https://myemail%40gmail.com:Password@api.huum.eu/action/home/status' data, err, hdrs = request(url) require('json') data = json.pdecode(data) log(data, err)

With the result:

* arg: 1
  * nil
* arg: 2
  * string: missing realm/nonce from response


RE: Digest Auth - JSON - admin - 17.07.2023

You need to use basic auth instead of digest. Simply use socket.http.request


RE: Digest Auth - JSON - Joep - 19.07.2023

(17.07.2023, 15:20)admin Wrote: You need to use basic auth instead of digest. Simply use socket.http.request

I got a 403 error back. If i try the complete url in the webbrowser it works correct. I replaced the @ with %40 in the username of the url.

Sauna Huum status 19.07.2023 14:49:57
* string: {"state":{"code":403,"message":"Error 403. Access denied."}}


RE: Digest Auth - JSON - admin - 19.07.2023

Try passing username/password like in this example: https://forum.logicmachine.net/showthread.php?tid=3936&pid=26618#pid26618
Try both username variants - with @ and %40.


RE: Digest Auth - JSON - Joep - 19.07.2023

(19.07.2023, 13:04)admin Wrote: Try passing username/password like in this example: https://forum.logicmachine.net/showthread.php?tid=3936&pid=26618#pid26618
Try both username variants - with @ and %40.

Yes that works like a charm Smile

Code:
http = require('socket.http') mime = require('mime') json = require('json') result = http.request({   url = 'http://api.huum.eu/action/home/status',   headers = {     Authorization = 'Basic ' .. mime.b64('username:password')   } }) data = json.pdecode(result) log(data)

And now my next question is how to send a JSON POST command?

The url is then: https://api.huum.eu/action/home/start
And the JSON command: {'targetTemperature' : 80}

(19.07.2023, 13:55)Joep Wrote:
(19.07.2023, 13:04)admin Wrote: Try passing username/password like in this example: https://forum.logicmachine.net/showthread.php?tid=3936&pid=26618#pid26618
Try both username variants - with @ and %40.

Yes that works like a charm Smile

Code:
http = require('socket.http') mime = require('mime') json = require('json') result = http.request({   url = 'http://api.huum.eu/action/home/status',   headers = {     Authorization = 'Basic ' .. mime.b64('username:password')   } }) data = json.pdecode(result) log(data)

And now my next question is how to send a JSON POST command?

The url is then: https://api.huum.eu/action/home/start
And the JSON command: {'targetTemperature' : 80}

I already found the solution. Smile

Code:
function post(url, body)   local ltn12 = require('ltn12')   local http = require('socket.http')   local sink = {}   local res, err = http.request({     url = url,     method = 'POST',     headers = {       ['Authorization'] = 'Basic '..(mime.b64('username:password')),       ['Content-Length'] = #body,       ['Content-Type'] = 'application/json',     },     sink = ltn12.sink.table(sink),     source = ltn12.source.string(body),   })   if res then     return table.concat(sink)   else     return nil, err   end end require('json') url = 'https://api.huum.eu/action/home/start' body = '{"targetTemperature" : 70}' result = post(url, body) data = json.pdecode(result) log(data)