chore: import upstream snapshot with attribution
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Helper script to complete Claude OAuth flow with proper PKCE."""
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import secrets
|
||||
import sys
|
||||
import urllib.parse
|
||||
import requests
|
||||
|
||||
CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
|
||||
AUTHORIZE_URL = "https://claude.ai/oauth/authorize"
|
||||
TOKEN_URL = "https://console.anthropic.com/v1/oauth/token"
|
||||
REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback"
|
||||
SCOPES = "org:create_api_key user:profile user:inference"
|
||||
|
||||
def generate_pkce():
|
||||
"""Generate PKCE verifier and challenge."""
|
||||
verifier = secrets.token_urlsafe(48)[:64] # 64 chars
|
||||
digest = hashlib.sha256(verifier.encode()).digest()
|
||||
challenge = base64.urlsafe_b64encode(digest).rstrip(b'=').decode()
|
||||
return verifier, challenge
|
||||
|
||||
def generate_state():
|
||||
"""Generate random state for CSRF protection."""
|
||||
return secrets.token_hex(16)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--exchange":
|
||||
# Exchange mode: read code from stdin or arg
|
||||
code = sys.argv[2] if len(sys.argv) > 2 else input("Enter code: ").strip()
|
||||
|
||||
# Load saved state
|
||||
with open("/tmp/claude_oauth_state.json") as f:
|
||||
saved = json.load(f)
|
||||
|
||||
# Exchange code for tokens
|
||||
resp = requests.post(TOKEN_URL, data={
|
||||
"grant_type": "authorization_code",
|
||||
"client_id": CLIENT_ID,
|
||||
"code": code,
|
||||
"code_verifier": saved["verifier"],
|
||||
"redirect_uri": REDIRECT_URI,
|
||||
})
|
||||
|
||||
if resp.status_code != 200:
|
||||
print(f"Error: {resp.text}")
|
||||
sys.exit(1)
|
||||
|
||||
tokens = resp.json()
|
||||
print(f"\nAccess token: {tokens['access_token'][:50]}...")
|
||||
print(f"Refresh token: {tokens['refresh_token'][:50]}...")
|
||||
|
||||
# Save in Claude Code format
|
||||
creds_dir = os.path.expanduser("~/.claude")
|
||||
os.makedirs(creds_dir, exist_ok=True)
|
||||
|
||||
import time
|
||||
expires_at = int(time.time() * 1000) + (tokens["expires_in"] * 1000)
|
||||
|
||||
creds = {
|
||||
"claudeAiOauth": {
|
||||
"accessToken": tokens["access_token"],
|
||||
"refreshToken": tokens["refresh_token"],
|
||||
"expiresAt": expires_at,
|
||||
}
|
||||
}
|
||||
|
||||
with open(os.path.join(creds_dir, ".credentials.json"), "w") as f:
|
||||
json.dump(creds, f, indent=2)
|
||||
|
||||
print(f"\nCredentials saved to ~/.claude/.credentials.json")
|
||||
else:
|
||||
# Generate new auth URL
|
||||
verifier, challenge = generate_pkce()
|
||||
state = generate_state()
|
||||
|
||||
params = {
|
||||
"response_type": "code",
|
||||
"client_id": CLIENT_ID,
|
||||
"redirect_uri": REDIRECT_URI,
|
||||
"scope": SCOPES,
|
||||
"code_challenge": challenge,
|
||||
"code_challenge_method": "S256",
|
||||
"state": state,
|
||||
}
|
||||
auth_url = f"{AUTHORIZE_URL}?{urllib.parse.urlencode(params)}"
|
||||
|
||||
with open("/tmp/claude_oauth_state.json", "w") as f:
|
||||
json.dump({
|
||||
"verifier": verifier,
|
||||
"challenge": challenge,
|
||||
"state": state,
|
||||
"auth_url": auth_url
|
||||
}, f)
|
||||
|
||||
print(f"Auth URL: {auth_url}")
|
||||
print(f"\nState saved to /tmp/claude_oauth_state.json")
|
||||
print(f"Verifier: {verifier}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user