Problem
httr2 currently treats an OAuth token as expired five seconds before expires_at. Some resource servers require a larger safety margin. In particular, Azure Databricks rejects tokens with 30 seconds or less remaining; the Databricks Python SDK therefore uses a 40-second margin.
This leaves a window where req_oauth_client_credentials() and req_oauth_auth_code() can reuse a token that httr2 considers valid but the resource server rejects. The normal OAuth reauthentication path does not help here because Databricks returns 403 rather than a 401 response with WWW-Authenticate: error="invalid_token".
This surfaced in databrickslabs/brickster#254. A downstream workaround can inspect and clear the request cache, but that relies on private request internals and can discard a U2M refresh token, causing the original authorization flow to run instead of refreshing.
References:
Proposed API
Add an argument such as expiry_margin = 5 (in seconds) to the request-based req_oauth_*() helpers and thread it through the existing token_has_expired(token, delay = ...) check. Keeping five seconds as the default would preserve current behavior while allowing clients such as brickster to request a 40-second margin without manipulating the cache.
I am happy to submit a PR with tests and documentation if this API direction seems reasonable.
Problem
httr2currently treats an OAuth token as expired five seconds beforeexpires_at. Some resource servers require a larger safety margin. In particular, Azure Databricks rejects tokens with 30 seconds or less remaining; the Databricks Python SDK therefore uses a 40-second margin.This leaves a window where
req_oauth_client_credentials()andreq_oauth_auth_code()can reuse a token thathttr2considers valid but the resource server rejects. The normal OAuth reauthentication path does not help here because Databricks returns 403 rather than a 401 response withWWW-Authenticate: error="invalid_token".This surfaced in databrickslabs/brickster#254. A downstream workaround can inspect and clear the request cache, but that relies on private request internals and can discard a U2M refresh token, causing the original authorization flow to run instead of refreshing.
References:
Proposed API
Add an argument such as
expiry_margin = 5(in seconds) to the request-basedreq_oauth_*()helpers and thread it through the existingtoken_has_expired(token, delay = ...)check. Keeping five seconds as the default would preserve current behavior while allowing clients such as brickster to request a 40-second margin without manipulating the cache.I am happy to submit a PR with tests and documentation if this API direction seems reasonable.