I can't understand where I should write down username and password? Everytime I get 401 Unauthorized error as response.
I've never looked into this before. What would you use as the URL if you were entering directly in your browser?
Digest auth requires the credentials to be encoded in request headers, if I recall correctly. (Encoded, not just supplied.) You'll have to look up the HTTP standards for how to do this, if no one has it off the top of their heads. (I do not.)
Unfortunately it looks like digest authentication is not supported in either the binding or system.net.httpClient.
Hmm, I must have misunderstood the ticket I found for this.
I guess you have to put the precomputed value there, rather than supplying a username/password and it being computed for you.
(I'm not really sure how this is supposed to be obtained unless you end up making a digest-authenticated request from another HTTP client / tool and just copy it in)
Yeah, I'm gonna go ahead and stand by my original response... despite its appearance in the UI, it's essentially unsupported. It shouldn't be there.
Oh, ok.
So it is also necessary to remove the mention of this in the documentation
https://docs.inductiveautomation.com/display/DOC81/HTTP+Bindings+in+Perspective
Yes.
There's a link at the bottom of each doc page:
Found a problem? Send us an email: docs@inductiveautomation.com
I've also opened a ticket for this and mentioned both the configuration UI and the user manual.
Would it be possible to get around this issue in the mean time with the python "requests" header?
And if so, how would I go about adding that to the scripting capabilities?
I know this is a little after the post has slowed down, please forgive me.
I can't take full credit for this, except for mostly the determination part... between myself and some AI assistance for library syntax (I didn't have any history with the hashlib or constructing digest headers) I put together this headers handshake for digestive using the httpClient and some integrated libraries. The example below is for an Axis camera, but I expect the concept would work for other digest situations. I was able to resolve the resulting data from the function into a perspective image control, so it seems functional. Don't forget to change the username and password.
import base64
import json
import re
import hashlib
import random
#constants
vapixClient = system.net.httpClient() #API Http Client
resolution = '1280x720'
uriRoot = 'http://'
uriImageJpg = '/axis-cgi/jpg/image.cgi?resolution='
username = "#####"
password = "#####"
def GetSnapshotFromCamera(IPAddress):
uri = uriRoot + IPAddress + uriImageJpg + resolution
#Send unauthenticated request
response = vapixClient.get(uri, timeout=20000)
if response:
status = response.getStatusCode()
auth_header = str(response.getHeaders().get("WWW-Authenticate"))
if "Digest" in auth_header:
#Parse required values from the auth challenge
realm = re.search(r'realm="([^"]+)"', auth_header).group(1)
nonce = re.search(r'nonce="([^"]+)"', auth_header).group(1)
qop = re.search(r'qop="([^"]+)"', auth_header)
qop = qop.group(1) if qop else "auth"
#Create HA1, HA2, and response hash
ha1 = hashlib.md5("{}:{}:{}".format(username, realm, password)).hexdigest()
ha2 = hashlib.md5("GET:{}".format(uri)).hexdigest()
nc = "00000001" # Nonce count
cnonce = str(random.randint(0, 99999999)) # Client nonce
response_hash = hashlib.md5("{}:{}:{}:{}:{}:{}".format(ha1, nonce, nc, cnonce, qop, ha2)).hexdigest()
#Construct Digest Authorization header
auth_header_value = (
'Digest username="{}", realm="{}", nonce="{}", uri="{}", '
'algorithm=MD5, response="{}", qop={}, nc={}, cnonce="{}"'
).format(username, realm, nonce, uri, response_hash, qop, nc, cnonce)
#Send the authenticated request
headers = {"Authorization": auth_header_value}
response = vapixClient.get(uri, headers=headers)
status = response.getStatusCode()
if status == 200:
data = response.getBody()
en64 = "data:image/jpg;base64,"+base64.b64encode(data)
return en64