-
Notifications
You must be signed in to change notification settings - Fork 4
Fix expired OAuth token and keychain lookup issues #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,9 +12,70 @@ model=$(echo "$input" | jq -r '.model.display_name') | |||||||
| CACHE_FILE="/tmp/claude-usage-cache.json" | ||||||||
| CACHE_MAX_AGE=60 | ||||||||
|
|
||||||||
| TOKEN_CACHE_FILE="/tmp/claude-oauth-token-cache.json" | ||||||||
| TOKEN_CACHE_MAX_AGE=3600 | ||||||||
|
|
||||||||
| get_access_token() { | ||||||||
| # Check token cache first (refreshed tokens are cached here) | ||||||||
| if [ -f "$TOKEN_CACHE_FILE" ]; then | ||||||||
| token_cache_age=$(($(date +%s) - $(stat -f %m "$TOKEN_CACHE_FILE" 2>/dev/null || echo 0))) | ||||||||
| if [ $token_cache_age -lt $TOKEN_CACHE_MAX_AGE ]; then | ||||||||
| cached_token=$(jq -r '.access_token // empty' "$TOKEN_CACHE_FILE" 2>/dev/null) | ||||||||
| if [ -n "$cached_token" ]; then | ||||||||
| echo "$cached_token" | ||||||||
| return | ||||||||
| fi | ||||||||
| fi | ||||||||
| fi | ||||||||
|
|
||||||||
| # Read credentials from Keychain - try current user account first, then fallback | ||||||||
| local acct | ||||||||
| acct=$(whoami) | ||||||||
| creds=$(security find-generic-password -s "Claude Code-credentials" -a "$acct" -w 2>/dev/null) | ||||||||
| if [ -z "$creds" ] || ! echo "$creds" | jq -e '.claudeAiOauth' >/dev/null 2>&1; then | ||||||||
| creds=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null) | ||||||||
| fi | ||||||||
|
|
||||||||
| access_token=$(echo "$creds" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null) | ||||||||
| refresh_token=$(echo "$creds" | jq -r '.claudeAiOauth.refreshToken // empty' 2>/dev/null) | ||||||||
| expires_at=$(echo "$creds" | jq -r '.claudeAiOauth.expiresAt // 0' 2>/dev/null) | ||||||||
| now_ms=$(($(date +%s) * 1000)) | ||||||||
|
|
||||||||
| # If token is still valid, use it | ||||||||
| if [ -n "$access_token" ] && [ "$now_ms" -lt "$expires_at" ] 2>/dev/null; then | ||||||||
| echo "$access_token" | ||||||||
| return | ||||||||
| fi | ||||||||
|
|
||||||||
| # Token expired - refresh it | ||||||||
| if [ -n "$refresh_token" ]; then | ||||||||
| refresh_result=$(curl -s -X POST "https://platform.claude.com/v1/oauth/token" \ | ||||||||
| -H "Content-Type: application/json" \ | ||||||||
| -d "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"$refresh_token\",\"client_id\":\"9d1c250a-e61b-44d9-88ed-5944d1962f5e\",\"scope\":\"user:profile user:inference user:sessions:claude_code user:mcp_servers\"}" 2>/dev/null) | ||||||||
|
|
||||||||
| new_token=$(echo "$refresh_result" | jq -r '.access_token // empty' 2>/dev/null) | ||||||||
| if [ -n "$new_token" ]; then | ||||||||
| # Cache the refreshed token | ||||||||
| echo "$refresh_result" > "$TOKEN_CACHE_FILE" | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Critical
OAuth tokens are written to - echo "$refresh_result" > "$TOKEN_CACHE_FILE"
+ echo "$refresh_result" > "$TOKEN_CACHE_FILE"
+ chmod 600 "$TOKEN_CACHE_FILE"
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fix it for me There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: OAuth tokens written to Prompt for AI agents
Suggested change
|
||||||||
|
|
||||||||
| # Update Keychain with new credentials | ||||||||
| new_refresh=$(echo "$refresh_result" | jq -r '.refresh_token // empty' 2>/dev/null) | ||||||||
| expires_in=$(echo "$refresh_result" | jq -r '.expires_in // 0' 2>/dev/null) | ||||||||
| new_expires_at=$(( $(date +%s) * 1000 + expires_in * 1000 )) | ||||||||
| [ -z "$new_refresh" ] && new_refresh="$refresh_token" | ||||||||
| updated_creds=$(echo "$creds" | jq --arg at "$new_token" --arg rt "$new_refresh" --argjson ea "$new_expires_at" \ | ||||||||
| '.claudeAiOauth.accessToken = $at | .claudeAiOauth.refreshToken = $rt | .claudeAiOauth.expiresAt = $ea') | ||||||||
| security delete-generic-password -s "Claude Code-credentials" -a "$acct" >/dev/null 2>&1 | ||||||||
| security add-generic-password -s "Claude Code-credentials" -a "$acct" -w "$updated_creds" 2>/dev/null | ||||||||
|
Comment on lines
+68
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Race condition: keychain delete/add isn't atomic; concurrent statusline calls could corrupt credentials Prompt To Fix With AIThis is a comment left during a code review.
Path: statusline-command.sh
Line: 68:69
Comment:
Race condition: keychain delete/add isn't atomic; concurrent statusline calls could corrupt credentials
How can I resolve this? If you propose a fix, please make it concise. |
||||||||
|
|
||||||||
| echo "$new_token" | ||||||||
| return | ||||||||
| fi | ||||||||
| fi | ||||||||
| } | ||||||||
|
|
||||||||
| fetch_usage() { | ||||||||
| # Get OAuth token from Keychain | ||||||||
| token=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null) | ||||||||
| token=$(get_access_token) | ||||||||
| if [ -n "$token" ]; then | ||||||||
| curl -s \ | ||||||||
| -H "Authorization: Bearer $token" \ | ||||||||
|
|
@@ -40,10 +101,20 @@ if [ -z "$api_usage" ]; then | |||||||
| fi | ||||||||
| fi | ||||||||
|
|
||||||||
| # Parse API usage | ||||||||
| # Parse API usage - handle both old and new API response formats | ||||||||
| five_hour_pct=$(echo "$api_usage" | jq -r '.five_hour.utilization // 0' 2>/dev/null | cut -d. -f1) | ||||||||
| seven_day_pct=$(echo "$api_usage" | jq -r '.seven_day.utilization // 0' 2>/dev/null | cut -d. -f1) | ||||||||
| opus_pct=$(echo "$api_usage" | jq -r '.seven_day_opus.utilization // 0' 2>/dev/null | cut -d. -f1) | ||||||||
| # Try seven_day_opus first, fall back to seven_day_sonnet | ||||||||
| opus_pct=$(echo "$api_usage" | jq -r '(.seven_day_opus.utilization // .seven_day_sonnet.utilization // 0)' 2>/dev/null | cut -d. -f1) | ||||||||
|
|
||||||||
| # Determine label for model-specific limit | ||||||||
| if echo "$api_usage" | jq -e '.seven_day_opus.utilization' >/dev/null 2>&1; then | ||||||||
| model_limit_label="Opus" | ||||||||
| elif echo "$api_usage" | jq -e '.seven_day_sonnet.utilization' >/dev/null 2>&1; then | ||||||||
| model_limit_label="Sonnet" | ||||||||
| else | ||||||||
| model_limit_label="Model" | ||||||||
| fi | ||||||||
|
|
||||||||
| # Get git branch | ||||||||
| cd "$project_dir" 2>/dev/null | ||||||||
|
|
@@ -91,9 +162,11 @@ fi | |||||||
| # Color code usage percentages (green < 60, yellow 60-89, red >= 90) | ||||||||
| color_pct() { | ||||||||
| local pct=$1 | ||||||||
| if [ "$pct" -ge 90 ]; then | ||||||||
| if [ -z "$pct" ] || [ "$pct" = "0" ]; then | ||||||||
| echo "\033[32m${pct}%\033[0m" # green | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: When Prompt for AI agents
Suggested change
|
||||||||
| elif [ "$pct" -ge 90 ] 2>/dev/null; then | ||||||||
| echo "\033[31m${pct}%\033[0m" # red | ||||||||
| elif [ "$pct" -ge 60 ]; then | ||||||||
| elif [ "$pct" -ge 60 ] 2>/dev/null; then | ||||||||
| echo "\033[33m${pct}%\033[0m" # yellow | ||||||||
| else | ||||||||
| echo "\033[32m${pct}%\033[0m" # green | ||||||||
|
|
@@ -105,13 +178,14 @@ seven_day_display=$(color_pct "$seven_day_pct") | |||||||
| opus_display=$(color_pct "$opus_pct") | ||||||||
|
|
||||||||
| # Output status line with clear labels | ||||||||
| # Format: repo | model | Context [bar] % | Tokens: N | Limits: 5hr% 7day% Opus% | (branch) | ||||||||
| printf "\033[33m%s\033[0m | %s | Context %b | Tokens: \033[36m%s\033[0m | Limits: 5hr %b · 7day %b · Opus %b | (\033[32m%s\033[0m)" \ | ||||||||
| # Format: repo | model | Context [bar] % | Tokens: N | Limits: 5hr% 7day% Model% | (branch) | ||||||||
| printf "\033[33m%s\033[0m | %s | Context %b | Tokens: \033[36m%s\033[0m | Limits: 5hr %b · 7day %b · %s %b | (\033[32m%s\033[0m)" \ | ||||||||
| "$repo_name" \ | ||||||||
| "$model" \ | ||||||||
| "$context_display" \ | ||||||||
| "$session_display" \ | ||||||||
| "$five_hour_display" \ | ||||||||
| "$seven_day_display" \ | ||||||||
| "$model_limit_label" \ | ||||||||
| "$opus_display" \ | ||||||||
| "$branch" | ||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stat -fis BSD/macOS-only; add Linux compatibility withstat -cPrompt To Fix With AI