X app setup
Human required. A person creates this app in a browser. An agent cannot click through the X developer portal for you.
Bookmarks are readable only with OAuth 2.0 user context. An app-only bearer token gets a 403 on that endpoint, which is why tweet-factory dropped the old OAuth 1.0a path.
In the developer portal
- Open developer.x.com and create an app (the free tier is enough for read access).
- Under User authentication settings set:
- App permissions: Read
- Type of App: Web App, Automated App or Bot (this is a confidential client, which is what returns a refresh token)
- Callback URI:
http://127.0.0.1:8675/callback - Website URL: any URL you control. The CLI does not call it.
- Save.
- Open Keys and tokens and copy the OAuth 2.0 Client ID and Client Secret into
.envasTWITTER_OAUTH2_CLIENT_IDandTWITTER_OAUTH2_CLIENT_SECRET.
The callback must match exactly, including the port 8675 and the path /callback. tf auth binds 127.0.0.1 unless you change it.
Scopes the CLI requests
tweet.read, users.read, like.read, bookmark.read, offline.access.
offline.access is the refresh token. Without it, the access token expires and every later fetch needs a new browser login. A public client (native app) often will not return a refresh token; use the confidential web-app type and keep the client secret.
Authorize
./tf authA browser opens. Log in as the account whose likes and bookmarks you want, and click Authorize app. The CLI writes .tokens.json (mode 0600) and prints the granted scopes plus whether a refresh token is present.
Headless:
./tf auth --no-browserOpen the printed URL on a machine with a browser. The callback still has to hit the process that is waiting. From another machine, set OAUTH_BIND_HOST and OAUTH_REDIRECT_URI to a reachable URL, register that exact URI on the app, and pass the same values (--bind, --redirect-uri). The default wait is 5 minutes (--timeout seconds).
Verify.
.tokens.jsonexists, is non-empty, and the auth log saidHas refresh token: true. File mode is600(stat -c '%a' .tokens.json).
One machine
The refresh token rotates every time it is used. Run tf fetch / tf refresh / tf cron on a single machine. A second checkout that refreshes the same token invalidates the first, and the next call returns 401. Fix that with one tf auth on the machine you intend to keep.
Verify. After auth,
./tf fetchprints a database summary withlike,bookmark, andowncounts (a brand-new account may legitimately have zeros).