Fix 401 authentication errors with automatic token refresh#25
Conversation
- Add automatic token refresh on 401 Unauthorized responses in API client - Preserve AWS credentials when refreshing tokens (required for MQTT) - Save refreshed tokens to cache after successful API calls - Add retry logic to prevent infinite retry loops This fix addresses the issue where cached tokens were rejected by the server with 401 errors but could still be refreshed. Previously, users would need to manually delete cached tokens or re-enter credentials. Now the system automatically refreshes tokens and retries the request. Fixes the 'API request failed: 401' error when using cached tokens.
There was a problem hiding this comment.
Pull Request Overview
This PR implements automatic token refresh on 401 authentication errors to prevent users from having to manually delete cached tokens or re-enter credentials when tokens expire.
Key changes:
- API client automatically detects 401 errors and refreshes tokens with single-retry logic
- Auth client preserves AWS credentials during token refresh (required for MQTT connectivity)
- CLI saves refreshed tokens back to cache for future use
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/nwp500/api_client.py |
Added automatic token refresh on 401 errors with retry logic to prevent infinite loops |
src/nwp500/auth.py |
Enhanced refresh_token() to preserve AWS credentials from old tokens since refresh responses don't include them |
src/nwp500/cli/__main__.py |
Added logic to persist refreshed tokens to cache after API calls |
- Catch only TokenRefreshError and AuthenticationError instead of Exception - Prevents masking unexpected errors during token refresh - Addresses code review feedback
cf11f74 to
3846d80
Compare
|
✅ Fixed in commit 3846d80 Changed the exception handler from catching all
This ensures we only catch expected authentication errors and don't mask unexpected errors during token refresh. |
src/nwp500/api_client.py
Outdated
| ) | ||
| try: | ||
| # Try to refresh the token | ||
| if self._auth_client.current_tokens: |
There was a problem hiding this comment.
Missing null check for refresh_token. If current_tokens exists but refresh_token is None/empty, this will pass None to refresh_token() which may cause unexpected behavior. Add validation: if self._auth_client.current_tokens and self._auth_client.current_tokens.refresh_token:
| if self._auth_client.current_tokens: | |
| if ( | |
| self._auth_client.current_tokens | |
| and self._auth_client.current_tokens.refresh_token | |
| ): |
| # Preserve AWS credentials from old tokens if not in refresh | ||
| # response |
There was a problem hiding this comment.
Comment formatting issue: multi-line comment should use proper continuation or be reformatted as a single line.
| # Preserve AWS credentials from old tokens if not in refresh | |
| # response | |
| # Preserve AWS credentials from old tokens if not in refresh response |
- Check that refresh_token exists and is not None/empty - Use local variable to avoid repeated attribute access - Log error when refresh_token is not available - Prevents passing None to refresh_token() method Addresses code review feedback
|
✅ Fixed in commit f87ab65 Added validation to check that tokens = self._auth_client.current_tokens
if tokens and tokens.refresh_token:
await self._auth_client.refresh_token(tokens.refresh_token)
# ... retry logic
else:
_logger.error("Cannot refresh token: refresh_token not available")Benefits:
|
Problem
The CLI was failing with 401 Unauthorized errors when using cached tokens. When tokens were rejected by the server but could still be refreshed, users had to manually delete the cached tokens file or re-enter their credentials.
Solution
This PR implements automatic token refresh on 401 errors with the following improvements:
Changes Made
API Client ()
_make_request()to detect 401 errors and attempt token refreshAuth Client ()
refresh_token()to preserve AWS credentials from old tokensCLI Main ()
get_first_device()which may trigger refreshHow It Works
~/.nwp500_tokens.jsonrefresh_token()with cached refresh tokenTesting
Benefits