Make cookie-file auth env-only for multi-account use
This commit is contained in:
13
README.md
13
README.md
@@ -174,22 +174,17 @@ twitter follow elonmusk --json
|
|||||||
twitter-cli uses this auth priority:
|
twitter-cli uses this auth priority:
|
||||||
|
|
||||||
1. **Environment variables**: `TWITTER_AUTH_TOKEN` + `TWITTER_CT0`
|
1. **Environment variables**: `TWITTER_AUTH_TOKEN` + `TWITTER_CT0`
|
||||||
2. **Cookie file**: `TWITTER_COOKIE_FILE` or `config.yaml -> auth.cookieFile`
|
2. **Cookie file**: `TWITTER_COOKIE_FILE`
|
||||||
3. **Browser cookies** (recommended): auto-extract from Arc/Chrome/Edge/Firefox/Brave
|
3. **Browser cookies** (recommended): auto-extract from Arc/Chrome/Edge/Firefox/Brave
|
||||||
|
|
||||||
If you already exported a Netscape-format `cookies.txt`, point the CLI at it:
|
If you already exported a Netscape-format `cookies.txt`, point the CLI at it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export TWITTER_COOKIE_FILE=/path/to/cookies.txt
|
TWITTER_COOKIE_FILE=/path/to/account-a.cookies.txt twitter whoami
|
||||||
twitter whoami
|
TWITTER_COOKIE_FILE=/path/to/account-b.cookies.txt twitter whoami
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in `config.yaml`:
|
This keeps the CLI multi-account friendly: no cookie path is pinned in config, so each command can target a different account cleanly.
|
||||||
|
|
||||||
```yaml
|
|
||||||
auth:
|
|
||||||
cookieFile: /path/to/cookies.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Browser extraction is recommended — it forwards ALL Twitter cookies (not just `auth_token` + `ct0`) and aligns request headers with your local runtime, which is closer to normal browser traffic than minimal cookie auth.
|
Browser extraction is recommended — it forwards ALL Twitter cookies (not just `auth_token` + `ct0`) and aligns request headers with your local runtime, which is closer to normal browser traffic than minimal cookie auth.
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
fetch:
|
fetch:
|
||||||
count: 50
|
count: 50
|
||||||
|
|
||||||
auth:
|
|
||||||
cookieFile: /mnt/shared/cookies.txt
|
|
||||||
|
|
||||||
filter:
|
filter:
|
||||||
mode: "topN"
|
mode: "topN"
|
||||||
topN: 20
|
topN: 20
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ def test_load_from_cookie_file_parses_netscape_cookie_dump(tmp_path) -> None:
|
|||||||
|
|
||||||
def test_get_cookies_uses_cookie_file_before_browser(monkeypatch) -> None:
|
def test_get_cookies_uses_cookie_file_before_browser(monkeypatch) -> None:
|
||||||
monkeypatch.setattr(auth, "load_from_env", lambda: None)
|
monkeypatch.setattr(auth, "load_from_env", lambda: None)
|
||||||
|
monkeypatch.setenv("TWITTER_COOKIE_FILE", "/tmp/cookies.txt")
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
auth,
|
auth,
|
||||||
"load_from_cookie_file",
|
"load_from_cookie_file",
|
||||||
@@ -116,7 +117,7 @@ def test_get_cookies_uses_cookie_file_before_browser(monkeypatch) -> None:
|
|||||||
lambda auth_token, ct0, cookie_string=None: seen.append((auth_token, ct0, cookie_string)) or {},
|
lambda auth_token, ct0, cookie_string=None: seen.append((auth_token, ct0, cookie_string)) or {},
|
||||||
)
|
)
|
||||||
|
|
||||||
cookies = auth.get_cookies({"auth": {"cookieFile": "/tmp/cookies.txt"}})
|
cookies = auth.get_cookies()
|
||||||
|
|
||||||
assert cookies["auth_token"] == "file-token"
|
assert cookies["auth_token"] == "file-token"
|
||||||
assert seen == [("file-token", "file-csrf", "a=1")]
|
assert seen == [("file-token", "file-csrf", "a=1")]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Supports:
|
Supports:
|
||||||
1. Environment variables: TWITTER_AUTH_TOKEN + TWITTER_CT0
|
1. Environment variables: TWITTER_AUTH_TOKEN + TWITTER_CT0
|
||||||
2. Cookie file: TWITTER_COOKIE_FILE or config auth.cookieFile (Netscape cookies.txt)
|
2. Cookie file: TWITTER_COOKIE_FILE (Netscape cookies.txt)
|
||||||
3. Auto-extract from browser via browser-cookie3
|
3. Auto-extract from browser via browser-cookie3
|
||||||
Extracts ALL Twitter cookies for full browser-like fingerprint.
|
Extracts ALL Twitter cookies for full browser-like fingerprint.
|
||||||
Prefers in-process extraction (required on macOS for Keychain access),
|
Prefers in-process extraction (required on macOS for Keychain access),
|
||||||
@@ -664,10 +664,7 @@ def get_cookies(config: Optional[Dict[str, Any]] = None) -> Dict[str, str]:
|
|||||||
|
|
||||||
# 2. Try cookie file from env/config
|
# 2. Try cookie file from env/config
|
||||||
if not cookies:
|
if not cookies:
|
||||||
auth_config = (config or {}).get("auth", {})
|
|
||||||
cookie_file = os.environ.get("TWITTER_COOKIE_FILE", "")
|
cookie_file = os.environ.get("TWITTER_COOKIE_FILE", "")
|
||||||
if not cookie_file and isinstance(auth_config, dict):
|
|
||||||
cookie_file = str(auth_config.get("cookieFile", "") or "")
|
|
||||||
cookies = load_from_cookie_file(cookie_file)
|
cookies = load_from_cookie_file(cookie_file)
|
||||||
if cookies:
|
if cookies:
|
||||||
logger.info("Loaded cookies from cookie file %s", cookie_file)
|
logger.info("Loaded cookies from cookie file %s", cookie_file)
|
||||||
@@ -687,7 +684,7 @@ def get_cookies(config: Optional[Dict[str, Any]] = None) -> Dict[str, str]:
|
|||||||
lines.extend(" " + line for line in hint.splitlines())
|
lines.extend(" " + line for line in hint.splitlines())
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("Option 1: Set TWITTER_AUTH_TOKEN and TWITTER_CT0 environment variables")
|
lines.append("Option 1: Set TWITTER_AUTH_TOKEN and TWITTER_CT0 environment variables")
|
||||||
lines.append("Option 2: Set TWITTER_COOKIE_FILE or config auth.cookieFile to a Netscape cookies.txt export")
|
lines.append("Option 2: Set TWITTER_COOKIE_FILE to a Netscape cookies.txt export")
|
||||||
lines.append("Option 3: Make sure you are logged into x.com in your browser (Arc/Chrome/Edge/Firefox/Brave)")
|
lines.append("Option 3: Make sure you are logged into x.com in your browser (Arc/Chrome/Edge/Firefox/Brave)")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("Run 'twitter -v <command>' for debug diagnostics.")
|
lines.append("Run 'twitter -v <command>' for debug diagnostics.")
|
||||||
|
|||||||
Reference in New Issue
Block a user