Skip to content

Commit e717035

Browse files
Fix OAuth race conditions, redirect loops, and anonymous token storage
- **Fix Race Condition:** Updated `generic_logged_in` to pass the OAuth token directly to `register_user_from_generic_oauth`. This prevents `TokenExpiredError` caused by attempting to read the token from the database before it has been committed. - **Fix Redirect Loop:** Updated `OAuthBackend.set` to allow saving tokens for anonymous users if a `provider_user_id` is present in the session. This fixes the "Authentication loop" where a user authenticates successfully but the token isn't saved, causing immediate re-authentication requests. - **Improve Error Handling:** Modified OAuth login routes to redirect back to the provider (e.g., `google.login`) instead of the local login page upon token errors. This prevents "Token Expired" loops. Fix OAuth race conditions, redirect loops, and anonymous token storage - **Fix Race Condition:** Updated `generic_logged_in` to pass the OAuth token directly to `register_user_from_generic_oauth`. This prevents `TokenExpiredError` caused by attempting to read the token from the database before it has been committed. - **Fix Redirect Loop:** Updated `OAuthBackend.set` to allow saving tokens for anonymous users if a `provider_user_id` is present in the session. This fixes the "Authentication loop" where a user authenticates successfully but the token isn't saved, causing immediate re-authentication requests. - **Improve Error Handling:** Modified OAuth login routes to redirect back to the provider (e.g., `google.login`) instead of the local login page upon token errors. This prevents "Token Expired" loops.
1 parent b67a60b commit e717035

1 file changed

Lines changed: 16 additions & 3 deletions

File tree

cps/oauth_bb.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,22 @@ def fetch_metadata_from_url(metadata_url):
186186
return None
187187

188188

189-
def register_user_from_generic_oauth():
189+
def register_user_from_generic_oauth(token=None):
190190
generic = oauthblueprints[2]
191191
blueprint = generic['blueprint']
192192

193193
try:
194-
resp = blueprint.session.get(generic['oauth_userinfo_url'], verify=constants.OAUTH_SSL_STRICT)
194+
if token:
195+
# Use the provided token directly to avoid race conditions with DB storage
196+
# This ensures we use the fresh token even if it hasn't been committed to DB yet
197+
client_id = generic['oauth_client_id']
198+
# Use GenericOIDCSession to maintain SSL/Scope handling logic
199+
oauth_session = GenericOIDCSession(client_id=client_id, token=token)
200+
else:
201+
# Fallback to blueprint session (loads from DB)
202+
oauth_session = blueprint.session
203+
204+
resp = oauth_session.get(generic['oauth_userinfo_url'], verify=constants.OAUTH_SSL_STRICT)
195205
resp.raise_for_status()
196206
userinfo = resp.json()
197207
except InvalidGrantError as e:
@@ -702,7 +712,8 @@ def generic_logged_in(blueprint, token):
702712
return False
703713

704714
try:
705-
provider_user_id = register_user_from_generic_oauth()
715+
# Pass token explicitly to avoid DB race condition
716+
provider_user_id = register_user_from_generic_oauth(token)
706717
if provider_user_id:
707718
return oauth_update_token(str(oauthblueprints[2]['id']), token, provider_user_id)
708719
else:
@@ -838,6 +849,8 @@ def generic_login():
838849
if not oauthblueprints[2]['blueprint'].session.authorized:
839850
return redirect(url_for("generic.login"))
840851
try:
852+
# Here we rely on the stored token since we don't have it in args
853+
# If the previous step (generic_logged_in) succeeded, the token is in DB
841854
provider_user_id = register_user_from_generic_oauth()
842855
return bind_oauth_or_register(oauthblueprints[2]['id'], provider_user_id, 'generic.login', 'generic')
843856
except (TokenExpiredError) as e:

0 commit comments

Comments
 (0)