From 53297dc885b1ce40593ed24a066140d8febb5d60 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Fri, 6 Mar 2026 11:56:41 -0800 Subject: [PATCH] defer PKCE verifier deletion until after all downstream processing Move _delete_pkce_verifier to after response_convertor and process_sso_jwt_access_token complete. If JWT processing raises, the verifier stays in cache so the user can retry without restarting the full OAuth flow. --- litellm/proxy/management_endpoints/ui_sso.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index a1223474401..78150895e53 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -842,10 +842,6 @@ async def get_generic_sso_response( redirect_url=redirect_url, additional_headers=additional_generic_sso_headers_dict, ) - # Token exchange succeeded — now it is safe to delete the single-use - # verifier. Deleting before the exchange would lose it on retries. - if pkce_cache_key: - await SSOAuthenticationHandler._delete_pkce_verifier(pkce_cache_key) # Pass the full response so custom response_convertor implementations # can access all fields (including id_token for claim extraction). result = response_convertor(combined_response, generic_sso) @@ -874,6 +870,12 @@ async def get_generic_sso_response( process_sso_jwt_access_token( access_token_str, sso_jwt_handler, result, role_mappings=role_mappings ) + # Delete the single-use PKCE verifier only after all downstream processing + # (response_convertor and process_sso_jwt_access_token) has completed + # successfully. Deleting earlier would consume the verifier on a transient + # failure, forcing the user to restart the entire OAuth flow from scratch. + if pkce_cache_key: + await SSOAuthenticationHandler._delete_pkce_verifier(pkce_cache_key) except Exception as e: error_message = str(e)