Skip to content

Commit

Permalink
Add service accounts (#140)
Browse files Browse the repository at this point in the history
* Add service accounts

And do not try to refresh identities that cannot be refreshed

* Do not let refresh go if no refresh token
  • Loading branch information
enolfc authored Oct 22, 2024
1 parent 2fa0db6 commit 0835136
Showing 1 changed file with 23 additions and 15 deletions.
38 changes: 23 additions & 15 deletions egi_notebooks_hub/egiauthenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import json
import os
import re
import time
from urllib.parse import urlencode

import jwt
Expand Down Expand Up @@ -245,6 +244,17 @@ def _validate_scope(self, proposal):
""",
)

# Service accounts may not have "sub", so this is an alternative
# claim for those accounts
servicename_claim = Unicode(
"client_id",
config=True,
help="""
Claim name to use for getting the name for services where the `username_claim`
is not available. See also `allow_anonymous`.
""",
)

allow_anonymous = Bool(
True,
config=True,
Expand Down Expand Up @@ -275,6 +285,10 @@ def user_info_to_username(self, user_info):
username = self.username_claim(user_info)
else:
username = user_info.get(self.username_claim, None)
if not username:
# try with the service name claim
username = user_info.get(self.servicename_claim, None)
# last attempt, go anonymous
if not username:
if not self.allow_anonymous:
message = (
Expand Down Expand Up @@ -381,11 +395,10 @@ async def authenticate(self, handler, data=None):
async def refresh_user(self, user, handler=None):
auth_state = await user.get_auth_state()
if not auth_state:
self.log.debug("No auth state, assuming user is not managed with Check-in")
self.log.debug("No auth state, assuming user is valid")
return True

access_token = auth_state.get("access_token", None)
refresh_token = auth_state.get("refresh_token", None)

if not access_token:
self.log.debug(
Expand Down Expand Up @@ -415,16 +428,9 @@ async def refresh_user(self, user, handler=None):
except jwt.exceptions.InvalidTokenError as e:
self.log.debug(f"Invalid access token, will try to refresh: {e}")

now = time.time()
refresh_info = auth_state.get("refresh_info", {})
# if the token is still valid, avoid refreshing
time_left = refresh_info.get("expiry_time", 0) - now
if time_left > self.auth_refresh_age:
self.log.debug("Credentials still valid, time left: %f", time_left)
return True

refresh_token = auth_state.get("refresh_token", None)
if not refresh_token:
self.log.debug("No refresh token, cannot refresh user")
self.log.warn(f"No refresh token, not allowing {user} without re-login")
return False

# performing the refresh token call
Expand Down Expand Up @@ -460,9 +466,11 @@ async def refresh_user(self, user, handler=None):
# clear here the existing auth state so it's no longer valid
await user.save_auth_state(None)
return False
refresh_info = json.loads(resp.body.decode("utf8", "replace"))
refresh_info["expiry_time"] = now + refresh_info["expires_in"]
auth_state["refresh_info"] = refresh_info
resp_body = resp.body.decode("utf8", "replace")
if not resp_body:
self.log.warning(f"Empty reply from refresh call for user {user}: {body}")
return False
refresh_info = json.loads(resp_body)
auth_state["access_token"] = refresh_info["access_token"]
if "refresh_token" in refresh_info:
auth_state["refresh_token"] = refresh_info["refresh_token"]
Expand Down

0 comments on commit 0835136

Please sign in to comment.