Skip to content

fix: keep the client's scope when refreshing after an expired token - #616

Open
Feldt wants to merge 1 commit into
dropbox:mainfrom
Feldt:fix/refresh-on-expired-token-scope
Open

Feldt wants to merge 1 commit into
dropbox:mainfrom
Feldt:fix/refresh-on-expired-token-scope

Conversation

@Feldt

@Feldt Feldt commented Oct 20, 2026

Copy link
Copy Markdown

Dropbox(scope=[...]) is documented as the "list of scopes to request on refresh". check_and_refresh_access_token() passes it on (refresh_access_token(scope=self._scope)), but the retry in request_json_string_with_retry() that runs after a 401 expired_access_token calls self.refresh_access_token() with no scope. That path runs when the server says the token has expired while the client still thinks it is valid, for example when the local clock is behind or the stored expiration is off. The /oauth2/token request then has no scope field, so the new token gets every scope of the original grant instead of the narrower set the client asked for.

This change passes scope=self._scope on that path too. Clients created without scope behave as before (scope=None, no scope field sent).

Test: TestClient.test_refresh_on_expired_access_token_uses_client_scope makes the first request raise AuthError with expired_access_token, lets the retry succeed, and checks the refresh POST body. On main it fails with assert None == 'files.metadata.read files.metadata.write'; with the fix it passes.

pytest test/unit/test_dropbox_unit.py::TestClient::test_refresh_on_expired_access_token_uses_client_scope
pytest test/unit/
tox

I prepared this change with an AI coding agent (Claude Code), and the agent ran the checks: tox with the full envlist (py311, py312, py313, pypy3, check, lint, docs, test_unit, coverage) passes, and pytest test/unit/ passes (111 tests) on Python 3.11, 3.12 and 3.13. I'm responsible for the change and will answer review.

Checklist

General Contributing

  • Have you read the Code of Conduct and signed the CLA?

Is This a Code Change?

  • Non-code related change (markdown/git settings etc)
  • SDK Code Change
  • Example/Test Code Change

Validation

  • Does tox pass?
  • Do the tests pass?

🤖 Generated with Claude Code

When an API call returns 401 expired_access_token, the retry loop in
request_json_string_with_retry refreshed the access token without the
scope given to Dropbox(scope=...). The token request then had no
"scope" field, so the new token carried every scope of the original
grant. Pass self._scope, as check_and_refresh_access_token already does.
@ineil-dbx

Copy link
Copy Markdown

Hi @Feldt, I’ve raised this with our engineering team for further review. They’ll be able to review the pull request and address it as appropriate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants