01-22-2019 03:20 AM
I am trying to use the Admin API.
I’m being told my Authorization signature is invalid with a HTTP 401 response code (“Invalid signature in request credentials” - 40103 to be precise).
The trouble is I’m following the guidance in the documentation here: Duo Admin API | Duo Security
… including using the same signing function, and the same sample data - but not getting the same signature back!
Is there anyone out there who’s got this working, and who can advise what I’m doing wrong?
Here’s my sample Python code - this doesn’t touch the API, but just calculates the Authorization signature based on the values in the example:
######################################################
import base64
from email import utils
import hmac
import hashlib
import urllib
import logging
import sys
import os
log = logging.getLogger(__name__)
out_hdlr = logging.StreamHandler(sys.stdout)
out_hdlr.setFormatter(logging.Formatter('%(asctime)s ' + os.path.basename(__file__) + ': %(message)s'))
out_hdlr.setLevel(logging.INFO)
log.addHandler(out_hdlr)
log.setLevel(logging.INFO)
def sign(method, host, path, params, skey, ikey):
"""
Return HTTP Basic Authentication ("Authorization" and "Date") headers.
method, host, path: strings from request
params: dict of request parameters
skey: secret key
ikey: integration key
"""
# create canonical string
#now = utils.formatdate()
now = "Tue, 21 Aug 2012 17:29:18 -0000"
log.debug(now)
canon = [now, method.upper(), host.lower(), path]
args = []
for key in sorted(params.keys()):
val = params[key]
if isinstance(val, unicode):
val = val.encode("utf-8")
args.append('%s=%s' % (urllib.quote(key, '~'), urllib.quote(val, '~')))
canon.append('&'.join(args))
canon = '\n'.join(canon)
log.info("Outputting canonical form of data to be signed")
log.info(canon)
# sign canonical string
sig = hmac.new(skey, canon, hashlib.sha1)
auth = '%s:%s' % (ikey, sig.hexdigest())
# return headers
return {'Date': now, 'Authorization': 'Basic %s' % base64.b64encode(auth)}
log.info("Starting Duo log retrieval")
params={"realname": "First Last",
"username": "root"}
headers=sign(method="POST",
host="api-xxxxxxxx.duosecurity.com",
path="/admin/v1/users",
params=params,
skey="Zh5eGmUq9zpfQnyUIu5OL9iWoMMv5ZNmk3zLJ4Ep",
ikey="DIWJ8X6AEYOR5OMC6TQ1")
log.info("Outputting headers")
log.info(headers)
#Gives output like:
#
# Starting Duo log retrieval
#
# Outputting canonical form of data to be signed
# Tue, 21 Aug 2012 17:29:18 -0000
# POST
# ■■■■ <- the correct host goes here, this has been obfuscated by Duo's Community forum
# /admin/v1/users
# realname=First%20Last&username=root
#
# Outputting headers
# {'Date': 'Tue, 21 Aug 2012 17:29:18 -0000', 'Authorization': 'Basic RElXSjhYNkFFWU9SNU9NQzZUUTE6YzFlZjQzNzY3YzNlYjNiMzI1OGRiZGRjYTZmOGQwOTQxZTA4NWI5Mg=='}
# THIS DOES NOT MATCH ONLINE EXAMPLE at https://duo.com/docs/adminapi#authentication
# Apparently Authorization should be RElXSjhYNkFFWU9SNU9NQzZUUTE6MmQ5N2Q2MTY2MzE5NzgxYjVhM2EwN2FmMzlkMzY2ZjQ5MTIzNGVkYw==
######################################################
01-22-2019 07:18 AM
That signature example value is not right. We will get that fixed. Go ahead and actually test your code against Duo, ignoring the example value.
01-22-2019 07:31 AM
Thanks for the response Kristina.
That’s unfortunate regarding the example being wrong - is my value correct then?
01-22-2019 07:56 AM
Because if its the sample code (“sign” function) that is incorrect then this would explain why this isn’t working for me in my production instance also…
06-12-2019 01:40 PM
So is the example on Auth API | Duo Security correct now?
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide