-
Notifications
You must be signed in to change notification settings - Fork 65
Description
Description
When using databricks auth token in a credential helper script (e.g. via Claude Code's apiKeyHelper), the CLI may return a cached OAuth access token that has only a few seconds of validity remaining. By the time the consuming process uses the token in an HTTP request, it is expired and the request fails with a 401.
The root cause is that the token validity check does not apply any expiry buffer. The underlying golang.org/x/oauth2 library provides an ExpiryDelta field on oauth2.Token (added in golang/oauth2@4d954e6) specifically for this purpose — when set, it causes the token to be treated as expired ExpiryDelta seconds before its actual expiry, triggering a proactive refresh. This field does not appear to be set anywhere in the SDK's token retrieval path.
Reproduction
// Minimal reproduction: call auth token in a loop and observe that
// tokens within the last ~10s of their lifetime are returned as valid.
cfg := &config.Config{Profile: "my-profile"}
tok, err := cfg.GetToken()
if err != nil {
log.Fatal(err)
}
remaining := time.Until(tok.Expiry)
fmt.Printf("Token expiry: %v (remaining: %v)\n", tok.Expiry, remaining)
// Observed: remaining can be < 10s, causing downstream 401s when the
// token is used after the few milliseconds it takes to make an API call.
In practice this is consistently hit when databricks auth token is invoked from a shell credential helper, as there is latency between the helper returning the token and the consuming process attaching it to a request.
Expected behavior
databricks auth token (and the underlying config.Config.GetToken()) should proactively refresh a token that is within a reasonable buffer window (suggested: 30–60 seconds) of expiry, rather than returning a token that is technically not-yet-expired but will be by the time it is used.
This can be achieved by setting token.ExpiryDelta before calling token.Valid() / token.Type() in the token cache retrieval path, e.g.:
token.ExpiryDelta = 60 * time.Second
if !token.Valid() {
// refresh ...
}
Is it a regression?
Not a regression — this buffer has never been set. Impact increases with credential helper use cases where there is latency between token retrieval and use (e.g. Claude Code apiKeyHelper, CI pipelines).
Debug Logs
N/A — the issue is in the caching logic before any SDK API calls are made.
Other Information
OS: macOS 14 (also reproducible on Linux)
Databricks CLI version: latest
golang.org/x/oauth2 reference: token.go#L66 — ExpiryDelta
Additional context
Workaround in use while awaiting a fix: decode the JWT exp claim in the credential helper script and manually invalidate the token cache entry (~/.databricks/token-cache.json) if the token has less than 60 seconds remaining, forcing the CLI to perform a refresh token exchange on the next invocation. This is fragile as it depends on internal cache file structure.
The permanent fix belongs in the SDK's token retrieval path so all consumers benefit automatically.