nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: jupyterhub/oauthenticator
Pull request #102 (2017-08-10 13:16:41)

urlsafe-base64-encode state
so we don't put a bunch of escapes in url parameters


Patch:
diff --git a/oauthenticator/oauth2.py b/oauthenticator/oauth2.py
index 92e0bad..94aa41d 100644
--- a/oauthenticator/oauth2.py
+++ b/oauthenticator/oauth2.py
@@ -4,11 +4,13 @@ Base classes for Custom Authenticator to use GitHub OAuth with JupyterHub
 Most of the code c/o Kyle Kelley (@rgbkrk)
 """
 
+import base64
 import json
 import os
 import uuid
 
 from tornado import gen, web
+from tornado.log import app_log
 
 from jupyterhub.handlers import BaseHandler
 from jupyterhub.auth import Authenticator
@@ -29,6 +31,31 @@ def guess_callback_uri(protocol, host, hub_server_url):
 
 STATE_COOKIE_NAME = 'oauthenticator-state'
 
+
+def _serialize_state(state):
+    """Serialize OAuth state to a base64 string after passing through JSON"""
+    json_state = json.dumps(state)
+    return base64.urlsafe_b64encode(
+        json_state.encode('utf8')
+    ).decode('ascii')
+
+
+def _deserialize_state(b64_state):
+    """Deserialize OAuth state as serialized in _serialize_state"""
+    if isinstance(b64_state, str):
+        b64_state = b64_state.encode('ascii')
+    try:
+        json_state = base64.urlsafe_b64decode(b64_state).decode('utf8')
+    except ValueError:
+        app_log.error("Failed to b64-decode state: %r", b64_state)
+        return {}
+    try:
+        return json.loads(json_state)
+    except ValueError:
+        app_log.error("Failed to json-decode state: %r", json_state)
+        return {}
+
+
 class OAuthLoginHandler(BaseHandler):
     """Base class for OAuth login handler
 
@@ -45,7 +72,7 @@ class OAuthLoginHandler(BaseHandler):
     def get_state(self):
         next_url = self.get_argument('next', None)
         if self._state is None:
-            self._state = json.dumps({
+            self._state = _serialize_state({
                 'state_id': uuid.uuid4().hex,
                 'next_url': next_url,
             })
@@ -121,7 +148,7 @@ class OAuthCallbackHandler(BaseHandler):
         """Get the redirect target from the state field"""
         state = self.get_state_url()
         if state:
-            return json.loads(state).get('next_url')
+            return _deserialize_state(state).get('next_url')
         # JupyterHub 0.8 adds default .get_next_url for a fallback
         if hasattr(BaseHandler, 'get_next_url'):
             return super().get_next_url()


Test patch:
diff --git a/oauthenticator/tests/test_oauth2.py b/oauthenticator/tests/test_oauth2.py
new file mode 100644
index 0000000..1e2616c
--- /dev/null
+++ b/oauthenticator/tests/test_oauth2.py
@@ -0,0 +1,13 @@
+import uuid
+
+from ..oauth2 import _serialize_state, _deserialize_state
+
+def test_serialize_state():
+    state1 = {
+        'state_id': uuid.uuid4().hex,
+        'next': 'url',
+    }
+    b64_state = _serialize_state(state1)
+    assert isinstance(b64_state, str)
+    state2 = _deserialize_state(b64_state)
+    assert state2 == state1