summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIdan Gazit <idan@gazit.me>2012-04-11 02:47:11 -0700
committerIdan Gazit <idan@gazit.me>2012-04-11 02:47:11 -0700
commit89c685be55e5656385e36d670f2e6ab19ce09696 (patch)
tree48bdc1c739d452a99c39b3c3e029c2874ab36582
parent018719878738cf3432a1398cffae29a9f781b327 (diff)
parentcb7587f7674db5d00df3a6134ad815fa03e2e7e6 (diff)
downloadoauthlib-89c685be55e5656385e36d670f2e6ab19ce09696.tar.gz
Merge pull request #17 from ib-lundgren/oauth2_tokens
Bearer and MAC token support
-rw-r--r--oauthlib/oauth2_draft25/tokens.py126
-rw-r--r--tests/oauth2_draft25/test_tokens.py84
2 files changed, 210 insertions, 0 deletions
diff --git a/oauthlib/oauth2_draft25/tokens.py b/oauthlib/oauth2_draft25/tokens.py
new file mode 100644
index 0000000..ed779c7
--- /dev/null
+++ b/oauthlib/oauth2_draft25/tokens.py
@@ -0,0 +1,126 @@
+from __future__ import absolute_import
+"""
+oauthlib.oauth2_draft25.tokens
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This module contains methods for adding two types of access tokens to requests.
+
+- Bearer http://tools.ietf.org/html/draft-ietf-oauth-saml2-bearer-08
+- MAC http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-00
+
+"""
+from binascii import b2a_base64
+import hashlib
+import hmac
+import time
+from urlparse import urlparse
+
+from . import utils
+
+def prepare_mac_header(token, uri, key, http_method, nonce=None, headers=None,
+ body=None, ext=u'', hash_algorithm=u'hmac-sha-1'):
+ """Add an `MAC Access Authentication`_ signature to headers.
+
+ Unlike OAuth 1, this HMAC signature does not require inclusion of the request
+ payload/body, neither does it use a combination of client_secret and
+ token_secret but rather a mac_key provided together with the access token.
+
+ Currently two algorithms are supported, "hmac-sha-1" and "hmac-sha-256",
+ `extension algorithms`_ are not supported.
+
+ Example MAC Authorization header, linebreaks added for clarity
+
+ Authorization: MAC id="h480djs93hd8",
+ nonce="1336363200:dj83hs9s",
+ mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
+
+ .. _`MAC Access Authentication`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
+ .. _`extension algorithms`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-7.1
+
+ :param uri: Request URI.
+ :param headers: Request headers as a dictionary.
+ :param http_method: HTTP Request method.
+ :param key: MAC given provided by token endpoint.
+ :param algorithm: HMAC algorithm provided by token endpoint.
+ :return: headers dictionary with the authorization field added.
+ """
+ http_method = http_method.upper()
+ host, port = utils.host_from_uri(uri)
+
+ if hash_algorithm.lower() == u'hmac-sha-1':
+ h = hashlib.sha1
+ else:
+ h = hashlib.sha256
+
+ nonce = nonce or u'{}:{}'.format(utils.generate_nonce(), utils.generate_timestamp())
+ sch, net, path, par, query, fra = urlparse(uri)
+
+ if query:
+ request_uri = path + u'?' + query
+ else:
+ request_uri = path
+
+ # Hash the body/payload
+ if body is not None:
+ bodyhash = b2a_base64(h(body).digest())[:-1].decode('utf-8')
+ else:
+ bodyhash = u''
+
+ # Create the normalized base string
+ base = []
+ base.append(nonce)
+ base.append(http_method.upper())
+ base.append(request_uri)
+ base.append(host)
+ base.append(port)
+ base.append(bodyhash)
+ base.append(ext)
+ base_string = '\n'.join(base) + u'\n'
+
+ # hmac struggles with unicode strings - http://bugs.python.org/issue5285
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ sign = hmac.new(key, base_string, h)
+ sign = b2a_base64(sign.digest())[:-1].decode('utf-8')
+
+ header = []
+ header.append(u'MAC id="%s"' % token)
+ header.append(u'nonce="%s"' % nonce)
+ if bodyhash: header.append(u'bodyhash="%s"' % bodyhash)
+ if ext: header.append(u'ext="%s"' % ext)
+ header.append(u'mac="%s"' % sign)
+
+ headers = headers or {}
+ headers[u'Authorization'] = u', '.join(header)
+ return headers
+
+def prepare_bearer_uri(token, uri):
+ """Add a `Bearer Token`_ to the request URI.
+ Not recommended, use only if client can't use authorization header or body.
+
+ http://www.example.com/path?access_token=h480djs93hd8
+
+ .. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
+ """
+ return utils.add_params_to_uri(uri, [((u'access_token', token))])
+
+def prepare_bearer_headers(token, headers=None):
+ """Add a `Bearer Token`_ to the request URI.
+ Recommended method of passing bearer tokens.
+
+ Authorization: Bearer h480djs93hd8
+
+ .. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
+ """
+ headers = headers or {}
+ headers[u'Authorization'] = u'Bearer %s' % token
+ return headers
+
+def prepare_bearer_body(token, body=u''):
+ """Add a `Bearer Token`_ to the request body.
+
+ access_token=h480djs93hd8
+
+ .. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
+ """
+ return utils.add_params_to_qs(body, [((u'access_token', token))])
diff --git a/tests/oauth2_draft25/test_tokens.py b/tests/oauth2_draft25/test_tokens.py
new file mode 100644
index 0000000..d605330
--- /dev/null
+++ b/tests/oauth2_draft25/test_tokens.py
@@ -0,0 +1,84 @@
+from __future__ import absolute_import
+
+from ..unittest import TestCase
+
+from oauthlib.oauth2_draft25.tokens import *
+
+class TokenTest(TestCase):
+
+ # MAC without body/payload or extension
+ mac_plain = {
+ u'token' : u'h480djs93hd8',
+ u'uri' : u'http://example.com/resource/1?b=1&a=2',
+ u'key' : u'489dks293j39',
+ u'http_method' : u'GET',
+ u'nonce' : u'264095:dj83hs9s',
+ u'hash_algorithm' : u'hmac-sha-1'
+ }
+ auth_plain = {
+ u'Authorization' : u'MAC id="h480djs93hd8", nonce="264095:dj83hs9s",'
+ ' mac="SLDJd4mg43cjQfElUs3Qub4L6xE="'
+ }
+
+ # MAC with body/payload, no extension
+ mac_body = {
+ u'token' : u'jd93dh9dh39D',
+ u'uri' : u'http://example.com/request',
+ u'key' : u'8yfrufh348h',
+ u'http_method' : u'POST',
+ u'nonce' : u'273156:di3hvdf8',
+ u'hash_algorithm' : u'hmac-sha-1',
+ u'body' : u'hello=world%21'
+ }
+ auth_body = {
+ u'Authorization' : u'MAC id="jd93dh9dh39D", nonce="273156:di3hvdf8",'
+ ' bodyhash="k9kbtCIy0CkI3/FEfpS/oIDjk6k=", mac="W7bdMZbv9UWOTadASIQHagZyirA="'
+ }
+
+ # MAC with body/payload and extension
+ mac_both = {
+ u'token' : u'h480djs93hd8',
+ u'uri' : u'http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b&c2&a3=2+q',
+ u'key' : u'489dks293j39',
+ u'http_method' : u'GET',
+ u'nonce' : u'264095:7d8f3e4a',
+ u'hash_algorithm' : u'hmac-sha-1',
+ u'body' : u'Hello World!',
+ u'ext' : u'a,b,c'
+ }
+ auth_both = {
+ u'Authorization' : u'MAC id="h480djs93hd8", nonce="264095:7d8f3e4a",'
+ ' bodyhash="Lve95gjOVATpfV8EL5X4nxwjKHE=", ext="a,b,c",'
+ ' mac="Z3C2DojEopRDIC88/imW8Ez853g="'
+ }
+
+
+ # Bearer
+ token = u'vF9dft4qmT'
+ uri = u'http://server.example.com/resource'
+ bearer_headers = {
+ u'Authorization' : u'Bearer vF9dft4qmT'
+ }
+ bearer_body = u'access_token=vF9dft4qmT'
+ bearer_uri = u'http://server.example.com/resource?access_token=vF9dft4qmT'
+
+
+ def test_prepare_mac_header(self):
+ """Verify mac signatures correctness
+
+ TODO: verify hmac-sha-256
+ """
+ self.assertEqual(prepare_mac_header(**self.mac_plain), self.auth_plain)
+ self.assertEqual(prepare_mac_header(**self.mac_body), self.auth_body)
+ self.assertEqual(prepare_mac_header(**self.mac_both), self.auth_both)
+
+
+ def test_prepare_bearer_request(self):
+ """Verify proper addition of bearer tokens to requests.
+
+ They may be represented as query components in body or URI or
+ in a Bearer authorization header.
+ """
+ self.assertEqual(prepare_bearer_headers(self.token), self.bearer_headers)
+ self.assertEqual(prepare_bearer_body(self.token), self.bearer_body)
+ self.assertEqual(prepare_bearer_uri(self.token, uri=self.uri), self.bearer_uri)