Claude Code 跨平台凭证迁移完整指南
WSL/Linux ↔ macOS 迁移 Claude Code 登录凭证的完整流程,含 macOS Keychain 多条目、HEX 编码、refreshToken 等实战细节。
Claude Code 在不同操作系统上对 OAuth 凭证的存储方式不同,跨平台迁移时容易出现「看起来已登录、实际 401」或「只能拿到 accessToken、无法自动刷新」等问题。本文覆盖 WSL/Linux → macOS 与 macOS → WSL/Linux 两个方向,并纠正早期文档中「macOS Keychain 只存 accessToken」的不完整结论。
.credentials.json 与 Keychain 中的 token 等同于你的账号凭证。迁移全程注意:
- 不要把凭证文件提交进 Git、上传网盘或贴进聊天工具。
- 经 Windows 盘 / scp 中转时,用完立刻删除中转副本。
- 目标机器上的文件权限保持
600。
各平台凭证存储机制
| 平台 | 存储方式 | 位置 | 内容特点 |
|---|---|---|---|
| Windows 原生 | 文件 | %USERPROFILE%\.claude\.credentials.json | 完整 JSON(accessToken + refreshToken) |
| Linux / WSL | 文件 | ~/.claude/.credentials.json | 完整 JSON(accessToken + refreshToken) |
| macOS | Keychain | 服务名:Claude Code-credentials | 可能有多条记录;正确账号下可含完整 JSON |
macOS 的特殊行为
- 登录成功后,往往会删除
~/.claude/.credentials.json(安全策略)。 - 实际 API 请求优先读 Keychain,而不是文件。
claude auth status有时会读文件,导致「状态显示已登录,对话却 401」的不同步现象。- Keychain 中可能存在多条同名服务记录,
acct不同,内容也不同。
完整凭证 JSON 结构(参考)
{
"claudeAiOauth": {
"accessToken": "sk-ant-oat01-...",
"refreshToken": "sk-ant-ort01-...",
"expiresAt": 1786206248795,
"refreshTokenExpiresAt": 1787923650795,
"scopes": [
"user:file_upload",
"user:inference",
"user:mcp_servers",
"user:profile"
]
}
}- 只有
accessToken→ 临时可用,过期后无法自动刷新。 - 同时有
refreshToken→ 可长期使用(refresh 机制正常时)。
关键发现:macOS Keychain 多条目问题
查看相关条目:
security dump-keychain 2>/dev/null | grep -A15 -i "Claude Code-credentials"常见会看到类似两条:
"acct"<blob>="claude"
"svce"<blob>="Claude Code-credentials"
"acct"<blob>="wudi" # 当前 macOS 用户名
"svce"<blob>="Claude Code-credentials"
分别读取的结果(实测):
# 经常是纯 accessToken 字符串
security find-generic-password -s "Claude Code-credentials" -a "claude" -w
# → sk-ant-oat01-xxxx
# 经常是完整 JSON(含 refreshToken)
security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w
# → {"claudeAiOauth":{"accessToken":"...","refreshToken":"sk-ant-ort01-...",...}}不指定 -a 时可能读到残缺条目。迁移时必须优先使用 -a "$(whoami)" 那条,它通常才是含 refreshToken 的完整凭证。
macOS 26 (Tahoe) 的 HEX 输出
在较新系统上,security ... -w 可能返回 HEX 字符串(如 7B22636C...),而不是直接 JSON。解码方式:
security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w | xxd -r -p方案 A:WSL / Linux → macOS
适用于:WSL 能正常用,macOS 登录失败或 token 过期。
步骤 1:在 WSL 打包
cd ~
tar -czf claude-backup.tar.gz .claude/
ls -lh claude-backup.tar.gz步骤 2:传到 macOS
# 方式一:scp
scp claude-backup.tar.gz <mac用户>@<macIP>:~/
# 方式二:经 Windows 中转
cp claude-backup.tar.gz /mnt/c/Users/<Windows用户名>/Desktop/步骤 3:在 macOS 解压
# 备份现有配置
mv ~/.claude ~/.claude.backup.$(date +%Y%m%d_%H%M%S) 2>/dev/null
mkdir -p ~/.claude
tar -xzf ~/claude-backup.tar.gz -C ~/
# 若出现嵌套 ~/.claude/.claude/,合并:
# cp -a ~/.claude/.claude/. ~/.claude/ && rm -rf ~/.claude/.claude
chmod 700 ~/.claude
chmod 600 ~/.claude/.credentials.json 2>/dev/null
ls -la ~/.claude/步骤 4:把凭证写入 Keychain(关键)
macOS 实际请求读的是 Keychain,只拷文件不够。
若 WSL 的 .credentials.json 是完整 JSON,直接把整段 JSON 写入 Keychain(而不是只写 accessToken),这样后续从 Mac 再导出时仍能拿到 refreshToken。
# 删除旧条目(可能有多条,可多执行几次)
security delete-generic-password -s "Claude Code-credentials" -a "claude" 2>/dev/null
security delete-generic-password -s "Claude Code-credentials" -a "$(whoami)" 2>/dev/null
# 把完整 JSON 写入(推荐)
CREDS=$(cat ~/.claude/.credentials.json)
security add-generic-password -U \
-s "Claude Code-credentials" \
-a "$(whoami)" \
-w "$CREDS"
# 验证
security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w如果手头只有 accessToken,也可退而求其次只写 token(过期后需重新登录):
TOKEN='sk-ant-oat01-你的accessToken'
security add-generic-password -U -s "Claude Code-credentials" -a "$(whoami)" -w "$TOKEN"步骤 5:验证
claude auth status
claude -p "Hello, test connection"方案 B:macOS → WSL / Linux
适用于:macOS 能正常用,WSL 需要同步登录状态。
步骤 1:确认哪条 Keychain 记录是完整的
echo "===== acct=claude ====="
security find-generic-password -s "Claude Code-credentials" -a "claude" -w 2>&1 | head -c 300
echo ""
echo "===== acct=$(whoami) ====="
security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w 2>&1 | head -c 300
echo ""判断:
| 输出 | 含义 | 处理 |
|---|---|---|
sk-ant-oat01-... | 仅 accessToken | 临时可用,无法自动刷新 |
以 { 开头且含 refreshToken | 完整凭证 | ✅ 使用这条 |
| 长串十六进制 | HEX 编码 JSON | 需 xxd -r -p 解码 |
步骤 2:导出到文件
mkdir -p ~/.claude
# 优先导出当前用户名那条
security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w > ~/.claude/.credentials.json
# 若是 HEX,改用:
# security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w | xxd -r -p > ~/.claude/.credentials.json
chmod 600 ~/.claude/.credentials.json
# 必须能解析,且同时含 accessToken 和 refreshToken
python3 -m json.tool ~/.claude/.credentials.json | head -20步骤 3:复制到 WSL
# 经 Windows 盘中转示例
cp ~/.claude/.credentials.json /mnt/c/Users/<Windows用户名>/
# 进入 WSL
mkdir -p ~/.claude
cp /mnt/c/Users/<Windows用户名>/.credentials.json ~/.claude/.credentials.json
chmod 600 ~/.claude/.credentials.json步骤 4:在 WSL 验证
python3 -m json.tool ~/.claude/.credentials.json | head -15
claude一键导出脚本(macOS)
#!/bin/bash
# export-claude-creds-from-mac.sh
set -e
mkdir -p ~/.claude
OUT="$HOME/.claude/.credentials.json"
RAW=$(security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w 2>/dev/null || true)
if [ -z "$RAW" ]; then
RAW=$(security find-generic-password -s "Claude Code-credentials" -a "claude" -w 2>/dev/null || true)
fi
if [ -z "$RAW" ]; then
echo "❌ 未找到 Claude Code-credentials"
exit 1
fi
# HEX 自动解码
if [[ "$RAW" =~ ^[0-9a-fA-F]+$ ]] && [ $((${#RAW} % 2)) -eq 0 ]; then
echo "$RAW" | xxd -r -p > "$OUT"
echo "ℹ️ 检测到 HEX,已解码"
else
printf '%s' "$RAW" > "$OUT"
fi
chmod 600 "$OUT"
if python3 -c "import json; d=json.load(open('$OUT')); print('ok')" 2>/dev/null; then
echo "✅ 已导出: $OUT"
python3 -m json.tool "$OUT" | head -20
else
echo "⚠️ 内容可能不是完整 JSON(可能只有 accessToken)"
head -c 120 "$OUT"; echo
fi常见问题与故障排查
Q1:claude auth status 显示已登录,对话却 401
原因:状态检查读了 .credentials.json,实际请求读了过期的 Keychain。处理:把有效 token / 完整 JSON 重新写入 Keychain,再测 claude -p "hi"。
Q2:security ... -w 只有 sk-ant-oat01-...
原因:读到了 acct=claude 残缺条目。处理:改用 -a "$(whoami)"。
Q3:导出后 python3 -m json.tool 报 Expecting value
原因:文件为空,或内容是未解码的 HEX。处理:用 xxd -r -p 解码后再写入。
Q4:WSL 里写了 .credentials.json 仍无法登录
- 确认文件权限为
600。 - 确认是完整 JSON(含
refreshToken),不是只有 accessToken。 - 清理干扰环境变量后重试:
unset CLAUDE_CODE_OAUTH_TOKEN ANTHROPIC_API_KEY ANTHROPIC_AUTH_TOKEN
claudeQ5:环境变量方式无效
部分版本对 CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_AUTH_TOKEN 支持不一致。优先使用完整 .credentials.json(Linux/WSL)或正确写入 Keychain(macOS)。
Q6:解压后出现 ~/.claude/.claude/ 嵌套
cp -a ~/.claude/.claude/. ~/.claude/
rm -rf ~/.claude/.claudeQ7:macOS 登录后 .credentials.json 被删
属预期行为。需要文件时:登录前备份,或登录后从 Keychain(指定正确 -a)重新导出。
命令速查
| 操作 | 命令 |
|---|---|
| 查看 Keychain 相关条目 | security dump-keychain 2>/dev/null | grep -A15 -i "Claude Code-credentials" |
| 读指定账号凭证 | security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w |
| 读 claude 账号凭证 | security find-generic-password -s "Claude Code-credentials" -a "claude" -w |
| HEX 解码 | ... -w | xxd -r -p |
| 删除旧条目 | security delete-generic-password -s "Claude Code-credentials" -a "$(whoami)" |
| 写入 Keychain | security add-generic-password -U -s "Claude Code-credentials" -a "$(whoami)" -w '...' |
| 验证 JSON | python3 -m json.tool ~/.claude/.credentials.json |
| 检查登录状态 | claude auth status |
| 实测对话 | claude -p "Hello" |
| 打包 WSL 配置 | cd ~ && tar -czf claude-backup.tar.gz .claude/ |
核心要点
- 平台差异:Linux/WSL 以
.credentials.json为准;macOS 以 Keychain 为准,文件常被自动删除。 - macOS Keychain 不是「只有 accessToken」:在
acct=$(whoami)下常能读到含refreshToken的完整 JSON,务必指定-a,避免读到残缺的acct=claude。 - 迁移关键动作:WSL → Mac 拷完配置后必须把凭证写入 Keychain;Mac → WSL 从 Keychain 正确账号导出完整 JSON 再放到
~/.claude/.credentials.json。 - 验证以实际对话为准:别只看
claude auth status,用claude -p "Hello"确认 API 真能通。 - 仅有 accessToken 的局限:临时可用,过期后无法自动刷新,需重新导出或重新登录。
文档根据实际踩坑整理,不同 Claude Code 版本行为可能略有差异。建议以「能完成一次真实对话」作为迁移成功标准。