-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathduo_auth.py
59 lines (48 loc) · 1.73 KB
/
duo_auth.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
"""
DuoAuth is a custom authentication class for the requests module.
It is basically a port of the official authentication (sign) function
to Python 3.
The best way to use it is with a Session object:
s = requests.Session()
s.auth = DuoAuth(skey=SKEY, ikey=IKEY)
"""
import base64
import email
import hashlib
import hmac
import urllib.parse
from requests.auth import AuthBase
class DuoAuth(AuthBase):
"""Attach HTTP Duo authentication to the given Request object."""
def __init__(self, skey, ikey):
self.skey = skey
self.ikey = ikey
def __call__(self, r):
url = urllib.parse.urlparse(r.url)
method = r.method.upper()
host = url.netloc.lower()
path = url.path
if method == 'GET':
params = urllib.parse.parse_qs(url.query)
elif method == 'POST':
params = urllib.parse.parse_qs(r.body)
else:
params = {}
# create canonical string
now = email.utils.formatdate()
canon = [now, method, host, path]
args = []
for key in sorted(params.keys()):
val = params[key][0]
args.append('{}={}'.format(urllib.parse.quote(key.encode(), '~'),
urllib.parse.quote(val.encode(), '~')))
canon.append('&'.join(args))
canon = '\n'.join(canon)
# sign canonical string
sig = hmac.new(self.skey.encode(), canon.encode(), hashlib.sha1)
auth = '{}:{}'.format(self.ikey, sig.hexdigest())
# add headers
r.headers['Date'] = now
r.headers['Authorization'] = 'Basic {}'.format(base64.b64encode(auth.encode()).decode())
r.headers['Content-Type'] = 'application/x-www-form-urlencoded'
return r