keycloakauthenticator 3.3.0__tar.gz → 4.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/PKG-INFO +16 -3
  2. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/README.md +1 -1
  3. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/_version.py +1 -1
  4. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/auth.py +15 -18
  5. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/tests/test_auth.py +11 -9
  6. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/PKG-INFO +16 -3
  7. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/requires.txt +1 -1
  8. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/setup.py +1 -1
  9. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/MANIFEST.in +0 -0
  10. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/__init__.py +0 -0
  11. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/metrics.py +0 -0
  12. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/tests/__init__.py +0 -0
  13. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/SOURCES.txt +0 -0
  14. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/dependency_links.txt +0 -0
  15. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/not-zip-safe +0 -0
  16. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/top_level.txt +0 -0
  17. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/pyproject.toml +0 -0
  18. {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: keycloakauthenticator
3
- Version: 3.3.0
3
+ Version: 4.0.1
4
4
  Summary: KeyCloakAuthenticator: Authenticate JupyterHub users with KeyCloak and OIDC
5
5
  Home-page: https://github.com/swan-cern/jupyterhub-extensions
6
6
  Author: SWAN Admins
@@ -15,6 +15,19 @@ Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Programming Language :: Python
16
16
  Classifier: Programming Language :: Python :: 3
17
17
  Description-Content-Type: text/markdown
18
+ Requires-Dist: jupyterhub
19
+ Requires-Dist: oauthenticator==16.1.0
20
+ Requires-Dist: PyJWT[crypto]>=2.0.0
21
+ Dynamic: author
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: platform
29
+ Dynamic: requires-dist
30
+ Dynamic: summary
18
31
 
19
32
  # KeyCloakAuthenticator
20
33
 
@@ -47,7 +60,7 @@ In your JupyterHub config file, set the authenticator and configure it:
47
60
  ```python
48
61
  # Enable the authenticator
49
62
  c.JupyterHub.authenticator_class = 'keycloakauthenticator.KeyCloakAuthenticator'
50
- c.KeyCloakAuthenticator.username_key = 'preferred_username'
63
+ c.KeyCloakAuthenticator.username_claim = 'preferred_username'
51
64
 
52
65
  # URL to redirect to after logout is complete with auth provider.
53
66
  c.KeyCloakAuthenticator.logout_redirect_url = 'https://cern.ch/swan'
@@ -29,7 +29,7 @@ In your JupyterHub config file, set the authenticator and configure it:
29
29
  ```python
30
30
  # Enable the authenticator
31
31
  c.JupyterHub.authenticator_class = 'keycloakauthenticator.KeyCloakAuthenticator'
32
- c.KeyCloakAuthenticator.username_key = 'preferred_username'
32
+ c.KeyCloakAuthenticator.username_claim = 'preferred_username'
33
33
 
34
34
  # URL to redirect to after logout is complete with auth provider.
35
35
  c.KeyCloakAuthenticator.logout_redirect_url = 'https://cern.ch/swan'
@@ -1,3 +1,3 @@
1
1
  # please don't modify this file,
2
2
  # this is automatically updated by bump2version
3
- __version__ = '3.3.0'
3
+ __version__ = '4.0.1'
@@ -153,8 +153,7 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
153
153
  # Try to load the configs until it succeeds
154
154
  while True:
155
155
  try:
156
- req = HTTPRequest(f"{self.oidc_issuer}/.well-known/openid-configuration", headers=self._get_headers())
157
- data = await self.fetch(req, "fetching oidc config")
156
+ data = await self.httpfetch(f"{self.oidc_issuer}/.well-known/openid-configuration", label="fetching oidc config")
158
157
 
159
158
  if not set(['authorization_endpoint', 'token_endpoint', 'userinfo_endpoint']).issubset(data.keys()):
160
159
  raise Exception('Unable to retrieve OIDC necessary values')
@@ -166,15 +165,15 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
166
165
  end_session_url = data.get('end_session_endpoint')
167
166
  if self.enable_logout and end_session_url:
168
167
  if self.logout_redirect_url:
169
- end_session_url += '?redirect_uri=%s' % self.logout_redirect_url
168
+ end_session_url += '?post_logout_redirect_uri=%s' % self.logout_redirect_url
169
+ end_session_url += '&client_id=%s' % self.client_id
170
170
  # Update parent class OAuthenticator.logout_redirect_url
171
171
  self.logout_redirect_url = end_session_url
172
172
 
173
173
  if self.config.check_signature :
174
174
  jwks_uri = data['jwks_uri']
175
175
 
176
- req = HTTPRequest(jwks_uri, headers=self._get_headers())
177
- jwk_data = await self.fetch(req, "fetching jwks")
176
+ jwk_data = await self.httpfetch(jwks_uri, label="fetching jwks")
178
177
  self.public_key = RSAAlgorithm(RSAAlgorithm.SHA256).from_jwk(jwk_data['keys'][0])
179
178
  self.log.info(f"aquired public key from {jwks_uri}")
180
179
  else:
@@ -185,7 +184,7 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
185
184
  self.log.info('KeycloakAuthenticator fully configured')
186
185
  break
187
186
  except:
188
- self.log.error("Failure to retrieve the openid configuration, will try again in 1 min (auth calls will fail)")
187
+ self.log.error("Failure to retrieve the openid configuration, will try again in 1 min (auth calls will fail)", exc_info=True)
189
188
  await asyncio.sleep(60)
190
189
 
191
190
  def _validate_roles(self, user_roles):
@@ -216,14 +215,14 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
216
215
  client_secret = self.client_secret,
217
216
  subject_token = token,
218
217
  audience = service_name,
219
- requested_token_type = 'urn:ietf:params:oauth:token-type:access_token'
218
+ requested_token_type = 'urn:ietf:params:oauth:token-type:access_token',
219
+ subject_token_type = 'urn:ietf:params:oauth:token-type:access_token'
220
220
  )
221
221
  data = parse.urlencode(values)
222
222
 
223
223
  req = HTTPRequest(
224
224
  self.token_url,
225
225
  method="POST",
226
- headers=self._get_headers(),
227
226
  body=data,
228
227
  )
229
228
 
@@ -250,7 +249,7 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
250
249
  access_tokens[service_name] = access_token
251
250
 
252
251
  # Produce logs and metrics for this token exchange
253
- queue_t = response.time_info['queue']
252
+ queue_t = response.time_info['queue'] if 'queue' in response.time_info else -1
254
253
  request_t = response.request_time
255
254
  self.log.info('Exchanged {} token, queue time: {} s, request time: {} s'.format(service_name, queue_t, request_t))
256
255
  metric_exchange_tornado_queue_time.labels("exchange_token_{}".format(service_name.replace("-","_"))).observe(queue_t)
@@ -269,16 +268,13 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
269
268
  refresh_token = refresh_token
270
269
  )
271
270
  data = parse.urlencode(values)
272
-
273
- req = HTTPRequest(
274
- self.token_url,
275
- method="POST",
276
- headers=self._get_headers(),
277
- body=data,
278
- )
279
271
  access_t = None
280
272
  refresh_t = None
281
- response = await self.fetch(req, "refreshing token", parse_json=False)
273
+ response = await self.httpfetch(self.token_url,
274
+ label="refreshing token",
275
+ method="POST",
276
+ body=data,
277
+ parse_json=False)
282
278
  if response.body:
283
279
  body = json.loads(response.body.decode('utf8', 'replace'))
284
280
  access_t = body.get('access_token', None)
@@ -289,7 +285,8 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
289
285
  self.log.error("Could not obtain refresh token for swan")
290
286
 
291
287
  metric_refresh_tornado_request_time.labels("refresh_token",response.code).observe(response.request_time)
292
- metric_refresh_tornado_queue_time.observe(response.time_info.get('queue'))
288
+ queue_t = response.time_info['queue'] if 'queue' in response.time_info else -1
289
+ metric_refresh_tornado_queue_time.observe(queue_t)
293
290
  self.log.info('Refresh token request completed in {} seconds'.format(time.time() - start))
294
291
  return access_t, refresh_t
295
292
 
@@ -50,19 +50,20 @@ async def test_refresh_user(monkeypatch):
50
50
  monkeypatch.setattr(KeyCloakAuthenticator, "oidc_issuer", "dummy-oidc-url")
51
51
 
52
52
  # Mock the response from the server on refresh and exchange tokens
53
- async def _mock_fetch(self, request, label, parse_json):
54
- print("Mocking fetch for ", request, label)
53
+ async def _mock_fetch(self, req, label, **kargs):
54
+ print(f"Mocking fetch for {req} ({label})")
55
55
  mock_response_body = json.dumps({
56
56
  "access_token": _get_mock_token(private_key, "new_access_token"),
57
57
  "refresh_token": _get_mock_token(private_key, "new_refresh_token"),
58
58
  })
59
+
59
60
  class MockResponse:
60
61
  def __init__(self):
61
62
  self.code = 200
62
63
  self.body = mock_response_body.encode('utf-8')
63
64
  self.request_time = 0
64
- self.time_info ={
65
- "queue": 0
65
+ self.time_info = {
66
+ "queue": 0
66
67
  }
67
68
  return MockResponse()
68
69
  monkeypatch.setattr(KeyCloakAuthenticator, "fetch", _mock_fetch)
@@ -120,22 +121,23 @@ async def test_refresh_user_with_expired_refresh_token(monkeypatch):
120
121
  # Mock the response from the server on refresh and exchange tokens
121
122
  monkeypatch.setattr(KeyCloakAuthenticator, "oidc_issuer", "dummy-oidc-url")
122
123
 
123
- async def _mock_fetch(self, request, label, parse_json):
124
- print("Mocking fetch for ", request, label)
124
+ async def _mock_httpfetch(self, url, label, **kargs):
125
+ print(f"Mocking httpfetch for {url} ({label})")
125
126
  mock_response_body = json.dumps({
126
127
  "access_token": _get_mock_token(private_key, "new_access_token"),
127
128
  "refresh_token": _get_mock_token(private_key, "new_refresh_token"),
128
129
  })
130
+
129
131
  class MockResponse:
130
132
  def __init__(self):
131
133
  self.code = 200
132
134
  self.body = mock_response_body.encode('utf-8')
133
135
  self.request_time = 0
134
- self.time_info ={
135
- "queue": 0
136
+ self.time_info = {
137
+ "queue": 0
136
138
  }
137
139
  return MockResponse()
138
- monkeypatch.setattr(KeyCloakAuthenticator, "fetch", _mock_fetch)
140
+ monkeypatch.setattr(KeyCloakAuthenticator, "httpfetch", _mock_httpfetch)
139
141
 
140
142
  class MockUser:
141
143
  """Fake user object, with dummy authentication state from DB"""
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: keycloakauthenticator
3
- Version: 3.3.0
3
+ Version: 4.0.1
4
4
  Summary: KeyCloakAuthenticator: Authenticate JupyterHub users with KeyCloak and OIDC
5
5
  Home-page: https://github.com/swan-cern/jupyterhub-extensions
6
6
  Author: SWAN Admins
@@ -15,6 +15,19 @@ Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Programming Language :: Python
16
16
  Classifier: Programming Language :: Python :: 3
17
17
  Description-Content-Type: text/markdown
18
+ Requires-Dist: jupyterhub
19
+ Requires-Dist: oauthenticator==16.1.0
20
+ Requires-Dist: PyJWT[crypto]>=2.0.0
21
+ Dynamic: author
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: platform
29
+ Dynamic: requires-dist
30
+ Dynamic: summary
18
31
 
19
32
  # KeyCloakAuthenticator
20
33
 
@@ -47,7 +60,7 @@ In your JupyterHub config file, set the authenticator and configure it:
47
60
  ```python
48
61
  # Enable the authenticator
49
62
  c.JupyterHub.authenticator_class = 'keycloakauthenticator.KeyCloakAuthenticator'
50
- c.KeyCloakAuthenticator.username_key = 'preferred_username'
63
+ c.KeyCloakAuthenticator.username_claim = 'preferred_username'
51
64
 
52
65
  # URL to redirect to after logout is complete with auth provider.
53
66
  c.KeyCloakAuthenticator.logout_redirect_url = 'https://cern.ch/swan'
@@ -1,3 +1,3 @@
1
1
  jupyterhub
2
- oauthenticator==15.0.0
2
+ oauthenticator==16.1.0
3
3
  PyJWT[crypto]>=2.0.0
@@ -23,7 +23,7 @@ setup_args = dict(
23
23
  packages=setuptools.find_packages(),
24
24
  install_requires=[
25
25
  'jupyterhub',
26
- 'oauthenticator==15.0.0',
26
+ 'oauthenticator==16.1.0',
27
27
  'PyJWT[crypto]>=2.0.0'
28
28
  ],
29
29
  zip_safe=False,