fix(settings): validate GitHub Personal Access Token before saving - #139
Conversation
WalkthroughThe Settings page now validates trimmed GitHub PATs through the rate-limit endpoint before saving them. It reports invalid-token and network errors, clears errors after edits or deletion, and prevents concurrent actions during validation. ChangesGitHub PAT validation
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/SettingsPage.jsx`:
- Around line 18-46: The handleSave validation flow can save a stale token and
permits concurrent submissions. Update handleSave and the associated token
input/save controls to prevent edits and reject duplicate saves while
isValidating is true; ensure every validation path restores the validation state
so only the currently submitted token can reach savePat.
- Around line 18-46: The handleSave response handling must distinguish
authentication failures from rate-limit or permission failures: show “Invalid
Personal Access Token” only when response.status is 401, and use a retryable
verification/rate-limit error for 403 and 429 (and other non-OK responses as
appropriate). Keep successful token saving and the existing network-error
handling unchanged.
- Around line 34-43: Update the PAT validation flow in SettingsPage around the
validation error handling and validating-state render to replace the visible
strings “Invalid Personal Access Token”, “Network error verifying token”, and
“Validating...” with app i18n lookups. Add corresponding keys and translations
to the existing translation resources, then use the established translation hook
or helper so all three labels are localized before rendering.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 8dd5ad03-41ea-4b43-afee-625d2915f1b1
📒 Files selected for processing (1)
src/pages/SettingsPage.jsx
| const handleSave = async () => { | ||
| const token = draft.trim(); | ||
| if (!token) return; | ||
|
|
||
| setIsValidating(true); | ||
| setTokenError(''); | ||
|
|
||
| try { | ||
| const response = await fetch('https://api.github.com/rate_limit', { | ||
| headers: { | ||
| Accept: 'application/vnd.github.v3+json', | ||
| Authorization: `token ${token}` | ||
| } | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| setTokenError('Invalid Personal Access Token'); | ||
| setIsValidating(false); | ||
| return; | ||
| } | ||
|
|
||
| savePat(token); | ||
| setSaved(true); | ||
| setTimeout(() => setSaved(false), 2000); | ||
| } catch (err) { | ||
| setTokenError('Network error verifying token'); | ||
| } finally { | ||
| setIsValidating(false); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Prevent stale validation from saving a different token.
The input remains editable while validation is pending. If token A is validating and the user changes the draft to token B, a successful response for token A calls savePat(token) while the input displays token B. Repeated Enter presses can also start concurrent validations because handleSave has no in-flight guard.
Disable token editing during validation and reject duplicate saves. Alternatively, associate a request identifier with the draft and discard stale responses before savePat.
Also applies to: 163-166
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/SettingsPage.jsx` around lines 18 - 46, The handleSave validation
flow can save a stale token and permits concurrent submissions. Update
handleSave and the associated token input/save controls to prevent edits and
reject duplicate saves while isValidating is true; ensure every validation path
restores the validation state so only the currently submitted token can reach
savePat.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
GitHub REST API authenticate with Personal Access Token rate limiting 403 404 401
💡 Result:
When working with the GitHub REST API using a Personal Access Token (PAT), errors often relate to authentication status, authorization scope, or rate limit policies [1][2]. 401 Unauthorized A 401 error indicates that your request is missing credentials, or the provided token is invalid, expired, or revoked [1][2]. Ensure you are including the token in the Authorization header (e.g., Authorization: Bearer ) and that the token is active [3][4]. 403 Forbidden A 403 error may occur for several reasons: - Primary Rate Limits: You have exceeded your hourly request limit (e.g., 5,000 requests per hour for authenticated users) [5][1]. The response may include a Retry-After header or indicate in the x-ratelimit-remaining header that your budget is exhausted [1][6]. - Secondary Rate Limits: These are triggered by rapid, concurrent, or computationally expensive requests [1][6][2]. - Authentication Rejection: If you submit multiple requests with invalid credentials in a short period, GitHub may temporarily block all authentication attempts for your account, responding with a 403 [3]. - Permissions: Your PAT may lack the specific scopes or organization-level permissions required to access the requested resource [1][7]. 404 Not Found A 404 error is returned if the resource does not exist, but it is also intentionally used when you attempt to access a private resource for which you are not authenticated (or lack permissions) [7][6]. This is a security measure to avoid confirming the existence of private repositories [7][6]. If you know the resource exists, verify your authentication and ensure your token has the correct scope to access that private data [7][6]. Handling Rate Limits - Primary Limits: Check the x-ratelimit-remaining header. If it is 0, do not retry until after the time specified in the x-ratelimit-reset header [1][6]. - Secondary Limits: Use exponential backoff if you receive a 403 or 429 error and no Retry-After header is provided [1][6][2]. - Conditional Requests: To optimize your rate limit usage, use ETag or Last-Modified headers to perform conditional requests, which do not count against your limit if the data has not changed [2].
Citations:
- 1: https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api?apiVersion=2026-03-10
- 2: https://errormedic.com/api/github-api/troubleshooting-github-api-rate-limit-403-429-and-authentication-401-errors
- 3: https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api?apiVersion=2026-03-10
- 4: https://docs.github.com/en/rest/using-the-rest-api/getting-started-with-the-rest-api?tool=cli
- 5: https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api
- 6: https://docs.github.com/en/enterprise-cloud@latest/rest/using-the-rest-api/troubleshooting-the-rest-api?apiVersion=2026-03-10
- 7: https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api
🌐 Web query:
site:docs.github.com/rest/authentication/authenticating-to-the-rest-api 401 bad credentials 403 rate limit
💡 Result:
- 401 Unauthorized / “Bad credentials”: The token or credentials are invalid. GitHub initially returns
401; repeated invalid attempts can temporarily result in403. [1] - 403 Forbidden: Usually means insufficient token permissions, SAML SSO authorization is missing, or you triggered a secondary/failed-login rate limit. [1][2]
- 403/429 rate limit: If
x-ratelimit-remaining: 0, wait until the Unix timestamp inx-ratelimit-reset. For secondary limits, honorretry-after; otherwise wait at least one minute and use exponential backoff. [2]
Check status with:
curl https://api.github.com/rate_limit[3]
🌐 Web query:
site:docs.github.com/rest/authentication/authenticating-to-the-rest-api 401 Bad credentials
💡 Result:
A 401 status code, officially defined as 401 Unauthorized, indicates that a request lacks valid authentication credentials for the target resource. While the HTTP standard specifies this as Unauthorized, it is colloquially referred to as Bad credentials because the error typically arises when the provided credentials—such as a username, password, API token, or access key—are incorrect, expired, or improperly formatted [1]. To resolve a 401 error, verify the following: 1. Credentials: Ensure the username, password, or API token being sent in the request headers or body is accurate [1]. 2. Authentication Method: Confirm that the server expects the authentication format being used, such as Basic Auth, Bearer Token, or an API-specific header [1]. 3. Expiration: Check if the access token has expired and if it needs to be refreshed. 4. Permissions: Ensure the account associated with the credentials has the necessary scope or permissions to access the requested resource [1].
Citations:
Distinguish token auth failures from rate-limit failures.
Invalid Personal Access Token should only be shown for 401 auth failures. 403 and 429 can mean rate limiting, permissions, or SSO/scopes rather than an invalid PAT, so display a retryable verification/rate-limit error for those statuses.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/SettingsPage.jsx` around lines 18 - 46, The handleSave response
handling must distinguish authentication failures from rate-limit or permission
failures: show “Invalid Personal Access Token” only when response.status is 401,
and use a retryable verification/rate-limit error for 403 and 429 (and other
non-OK responses as appropriate). Keep successful token saving and the existing
network-error handling unchanged.
| setTokenError('Invalid Personal Access Token'); | ||
| setIsValidating(false); | ||
| return; | ||
| } | ||
|
|
||
| savePat(token); | ||
| setSaved(true); | ||
| setTimeout(() => setSaved(false), 2000); | ||
| } catch (err) { | ||
| setTokenError('Network error verifying token'); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | sed -n '1,200p'
echo "== target file outline/sections =="
if [ -f src/pages/SettingsPage.jsx ]; then
wc -l src/pages/SettingsPage.jsx
sed -n '1,240p' src/pages/SettingsPage.jsx | nl -ba
fi
echo "== i18n/resource/string candidates =="
rg -n --glob '!node_modules/**' '('use client'|i18n|useTranslation|t\(|translation|locales|en|zh|messages|translations|Validating|Network error|Invalid Personal Access Token' src package.json . 2>/dev/null | sed -n '1,240p'Repository: AOSSIE-Org/OrgExplorer
Length of output: 2107
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== target file first 240 lines with line numbers =="
awk '{printf "%6d\t%s\n", NR, $0}' src/pages/SettingsPage.jsx | sed -n '1,240p'
echo "== search hardcoded strings and i18n usage =="
rg -n --glob '!node_modules/**' \
"'use client'|useTranslation|t\(|translation|locales|messages|Validating|Network error|Invalid Personal Access Token|Validating\\.\\.\\." \
src package.json eslint.config.js 2>/dev/null | sed -n '1,260p'
echo "== package/deps summary =="
python3 - <<'PY'
import json
p=json.load(open('package.json'))
for sec in ['dependencies','devDependencies','dependenciesExpo']:
vals=p.get(sec, {})
hits={k:v for k,v in vals.items() if 'i18n' in k.lower() or 'translate' in k.lower() or k.lower() in ['react-i18next','i18next','next-intl','expo-localization','expo-i18n']}
print(sec, hits)
PYRepository: AOSSIE-Org/OrgExplorer
Length of output: 35357
Externalize the PAT validation labels.
Invalid Personal Access Token, Network error verifying token, and Validating... are visible to users. Add them to translation resources and resolve them through the app’s i18n mechanism before rendering them at lines 34, 43, and 189.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/SettingsPage.jsx` around lines 34 - 43, Update the PAT validation
flow in SettingsPage around the validation error handling and validating-state
render to replace the visible strings “Invalid Personal Access Token”, “Network
error verifying token”, and “Validating...” with app i18n lookups. Add
corresponding keys and translations to the existing translation resources, then
use the established translation hook or helper so all three labels are localized
before rendering.
Source: Path instructions
Fixes #138
This updates the Settings page to verify the Personal Access Token with the GitHub API before saving it to local storage. It now displays an appropriate error message if the token is invalid and disables the save button while validating.
Summary by CodeRabbit