Repository: internetarchive/openlibrary

Issue:
make_ia_token access_key must be bytes
<!-- What issue does this PR close? -->
Closes https://sentry.archive.org/sentry/ol-web/issues/9699 and https://sentry.archive.org/sentry/ol-web/issues/9700 

<!-- What does this PR achieve? [feature|hotfix|fix|refactor] -->
Encodes the str `access_key` as bytes (if necessary) before calling [hmac.new](https://docs.python.org/3/library/hmac.html)

### Technical
<!-- What should be noted about the implementation? -->

### Testing
<!-- Steps for reviewer to reproduce/verify what this PR does/fixes. -->

### Screenshot
<!-- If this PR touches UI, please post evidence (screenshots) of it behaving correctly. -->

### Stakeholders
<!-- @ tag stakeholders of this bug -->

make_ia_token access_key must be bytes
<!-- What issue does this PR close? -->
Closes https://sentry.archive.org/sentry/ol-web/issues/9699 and https://sentry.archive.org/sentry/ol-web/issues/9700 

<!-- What does this PR achieve? [feature|hotfix|fix|refactor] -->
Encodes the str `access_key` as bytes (if necessary) before calling [hmac.new](https://docs.python.org/3/library/hmac.html)

### Technical
<!-- What should be noted about the implementation? -->

### Testing
<!-- Steps for reviewer to reproduce/verify what this PR does/fixes. -->

### Screenshot
<!-- If this PR touches UI, please post evidence (screenshots) of it behaving correctly. -->

### Stakeholders
<!-- @ tag stakeholders of this bug -->



Discussion:

Resolving patch:
diff --git a/openlibrary/plugins/upstream/borrow.py b/openlibrary/plugins/upstream/borrow.py
--- a/openlibrary/plugins/upstream/borrow.py
+++ b/openlibrary/plugins/upstream/borrow.py
@@ -3,10 +3,12 @@
 import copy
 import datetime
 import time
+import hashlib
 import hmac
 import re
 import requests
 import simplejson
+import six
 import logging
 
 import web
@@ -843,6 +845,26 @@ def get_ia_auth_dict(user, item_id, user_specified_loan_key, access_token):
     }
 
 
+def ia_hash(token_data):
+    access_key = make_access_key()
+    if six.PY3:
+        return hmac.new(
+            access_key,
+            token_data.encode('utf-8'),
+            hashlib.md5
+        ).hexdigest()
+    return hmac.new(access_key, token_data).hexdigest()
+
+
+def make_access_key():
+    try:
+        access_key = config.ia_access_secret
+        return access_key if six.PY3 else access_key.encode('utf-8')
+    except AttributeError:
+        raise RuntimeError(
+            "config value config.ia_access_secret is not present -- check your config"
+        )
+
 def make_ia_token(item_id, expiry_seconds):
     """Make a key that allows a client to access the item on archive.org for the number of
        seconds from now.
@@ -851,24 +873,12 @@ def make_ia_token(item_id, expiry_seconds):
     # $hmac = hash_hmac('md5', "{$id}-{$timestamp}", configGetValue('ol-loan-secret'));
     # return "{$timestamp}-{$hmac}";
 
-    try:
-        access_key = config.ia_access_secret
-    except AttributeError:
-        raise Exception("config value config.ia_access_secret is not present -- check your config")
-
     timestamp = int(time.time() + expiry_seconds)
     token_data = '%s-%d' % (item_id, timestamp)
-
-    token = '%d-%s' % (timestamp, hmac.new(access_key, token_data).hexdigest())
+    token = '%d-%s' % (timestamp, ia_hash(token_data))
     return token
 
 def ia_token_is_current(item_id, access_token):
-    try:
-        access_key = config.ia_access_secret
-    except AttributeError:
-        raise Exception("config value config.ia_access_secret is not present -- check your config")
-    if not isinstance(access_key, (bytes, bytearray)):
-        access_key = access_key.encode('utf-8')
 
     # Check if token has expired
     try:
@@ -888,7 +898,7 @@ def ia_token_is_current(item_id, access_token):
         return False
 
     expected_data = '%s-%s' % (item_id, token_timestamp)
-    expected_hmac = hmac.new(access_key, expected_data).hexdigest()
+    expected_hmac = ia_hash(expected_data)
 
     if token_hmac == expected_hmac:
         return True


Test patch: