Posts: 60
Threads: 22
Joined: Jul 2017
Reputation:
0
Hello!
Is there anyone that can provide me with a script to recieve a JSON table from an API using the Digest Auth?
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
Posts: 111
Threads: 14
Joined: Nov 2019
Reputation:
6
15.07.2023, 14:44
(This post was last modified: 15.07.2023, 15:07 by Joep.)
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.
Posts: 111
Threads: 14
Joined: Nov 2019
Reputation:
6
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
Posts: 111
Threads: 14
Joined: Nov 2019
Reputation:
6
(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
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
You need to use basic auth instead of digest. Simply use socket.http.request
Posts: 111
Threads: 14
Joined: Nov 2019
Reputation:
6
(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."}}
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
Try passing username/password like in this example: https://forum.logicmachine.net/showthrea...8#pid26618
Try both username variants - with @ and %40.
Posts: 111
Threads: 14
Joined: Nov 2019
Reputation:
6
19.07.2023, 13:55
(This post was last modified: 19.07.2023, 14:32 by Joep.)
(19.07.2023, 13:04)admin Wrote: Try passing username/password like in this example: https://forum.logicmachine.net/showthrea...8#pid26618
Try both username variants - with @ and %40.
Yes that works like a charm
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/showthrea...8#pid26618
Try both username variants - with @ and %40.
Yes that works like a charm
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.
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)
|