Skip to content

Commit db2e492

Browse files
committed
feat(ciba): handle the Retry-After header value upon slow_down error
1 parent f08d6d5 commit db2e492

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

packages/auth0-ai/auth0_ai/authorizers/ciba/ciba_authorizer_base.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,31 @@ async def _start(self, authorize_params) -> CIBAAuthorizationRequest:
154154
else:
155155
raise
156156

157+
def _extract_retry_after_header(self, error: Auth0Error) -> Optional[int]:
158+
"""
159+
Extract the Retry-After header value from an Auth0Error.
160+
161+
Args:
162+
error: The Auth0Error object that may contain HTTP headers
163+
164+
Returns:
165+
The retry-after value in seconds as an integer, or None if not present
166+
"""
167+
168+
if not hasattr(error, 'headers') or not error.headers:
169+
return None
170+
171+
retry_after = error.headers.get('retry-after') or error.headers.get('Retry-After')
172+
173+
if retry_after is None:
174+
return None
175+
176+
try:
177+
return int(retry_after)
178+
except (ValueError, TypeError):
179+
# If the retry-after value is not a valid integer, return None
180+
return None
181+
157182
def _get_credentials_internal(self, auth_request: CIBAAuthorizationRequest) -> TokenResponse | None:
158183
try:
159184
# Calculate elapsed time in seconds
@@ -180,7 +205,8 @@ def _get_credentials_internal(self, auth_request: CIBAAuthorizationRequest) -> T
180205
raise AuthorizationPendingInterrupt(e.message, auth_request)
181206

182207
if e.error_code == "slow_down":
183-
raise AuthorizationPollingInterrupt(e.message, auth_request)
208+
retry_after = self._extract_retry_after_header(e)
209+
raise AuthorizationPollingInterrupt(e.message, auth_request, retry_after)
184210

185211
if e.error_code == "invalid_grant":
186212
raise InvalidGrantInterrupt(e.message, auth_request)
@@ -203,7 +229,7 @@ async def get_credentials_polling(self, auth_request: CIBAAuthorizationRequest)
203229
try:
204230
credentials = self._get_credentials_internal(auth_request)
205231
except (AuthorizationPendingInterrupt, AuthorizationPollingInterrupt) as err:
206-
await asyncio.sleep(err.request["interval"])
232+
await asyncio.sleep(err.next_retry_interval())
207233
except Exception:
208234
raise
209235

packages/auth0-ai/auth0_ai/interrupts/ciba_interrupts.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,24 @@ def __init__(self, message: str, request: CIBAAuthorizationRequest):
7676
CIBAInterrupt.__init__(self, message, AuthorizationPendingInterrupt.code)
7777
WithRequestData.__init__(self, request)
7878

79+
def next_retry_interval(self) -> int:
80+
"""Return the interval in seconds to wait before the next retry attempt."""
81+
return self.request["interval"]
82+
7983

8084
class AuthorizationPollingInterrupt(CIBAInterrupt, WithRequestData):
8185
code: str = "CIBA_AUTHORIZATION_POLLING_ERROR"
8286

83-
def __init__(self, message: str, request: CIBAAuthorizationRequest):
87+
def __init__(self, message: str, request: CIBAAuthorizationRequest, retry_after: int = None):
8488
Auth0Interrupt.__init__(self, message, AuthorizationPollingInterrupt.code)
8589
WithRequestData.__init__(self, request)
90+
self.retry_after = retry_after
91+
92+
def next_retry_interval(self) -> int:
93+
"""Return the interval in seconds to wait before the next retry attempt."""
94+
# Use the retry_after value from the HTTP header if available,
95+
# otherwise fall back to the original interval from the auth request
96+
return self.retry_after if self.retry_after is not None else self.request["interval"]
8697

8798

8899
class InvalidGrantInterrupt(CIBAInterrupt, WithRequestData):

0 commit comments

Comments
 (0)