Thanks for the reply. I ended up finding that forum thread which got me on the right track. It was after I read that thread that I realized I wasn’t thinking about Twitter bots correctly. At first, I was thinking that after creating a developer account, I’d automatically get a “developer profile” so to speak. Now I realize that it’s up to me to create a new Twitter account for my bot if I want to tweet from a dedicated profile for the bot. I did that, went through the docs and sample code some more, and eventually got it working.
I was also misunderstanding how many steps are involved in OAuth. Once I realized I had to do a few steps to create the Authorization header for the OAuth flow for each automated request I want to make, I was able to take some of the sample JS code (just the part that uses the crypto and oauth-1.0a NPM modules) to console log an authorization header value which I could sub into the sample cURL requests in the docs.
My code:
import crypto from 'crypto';
import OAuth from 'oauth-1.0a';
try {
const oauth = OAuth({
consumer: {
key: 'ZR39xkPHqNsjlSX77gZtSGCcH',
secret: 'qzAXA269DM18DwivbKZzunW5uQesSweHNFNotTzxnlKeGdjgyV'
},
signature_method: 'HMAC-SHA1',
hash_function: (baseString, key) => crypto.createHmac('sha1', key).update(baseString).digest('base64')
});
const token = {
key: '1477507964298739712-VFdoDZug9ifkvA9GX89rEqoywZnblu',
secret: 'amKCrk4X7XKNEqwdDR901sBRy2WhgXr5CrxKWc5bhRuBf'
};
const authHeader = oauth.toHeader(oauth.authorize({
url: `https://api.twitter.com/2/tweets`,
method: 'POST'
}, token));
console.log(authHeader);
} catch (e) {
console.log(e);
process.exit(-1);
}
Output was like:
{
Authorization: 'OAuth oauth_consumer_key="<redacted>", oauth_nonce="<redacted>", oauth_signature="<redacted>", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1641103564", oauth_token="<redacted>", oauth_version="1.0"'
}
The docs at /en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets were slightly incorrect though. They have a sample cURL:
curl -X POST https://api.twitter.com/2/tweets -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-type: application/json" -d '{"text": "Hello World!"}'
Which I found I had to change to:
curl -X POST https://api.twitter.com/2/tweets -H "Authorization: $ACCESS_TOKEN" -H "Content-type: application/json" -d '{"text": "Hello World!"}'
Otherwise I got an error:
{
"title": "Unsupported Authentication",
"detail": "Authenticating with OAuth 2.0 Application-Only is forbidden for this endpoint. Supported authentication types are [OAuth 1.0a User Context, OAuth 2.0 User Context].",
"type": "https://api.twitter.com/2/problems/unsupported-authentication",
"status": 403
}
I also found that in order to finish setting up the app and get the token and secret, I had to put some value in for “callback URI” and “website URL” in the portal, which were required, even though I had no application for my users (I have no users). So I used “https://example.com” for both required values.
I think for my use case, it was my inexperience with OAuth that made it hard to implement my use case. I was remembering simple, non-OAuth APIs I’d used in the past where I just needed to get a client key, which I could then use in any request I make until I deactivated the client key.
Perhaps Twitter could make the docs a bit easier for someone like me. The docs could explain that because it’s OAuth based, the developer needs to create a new Twitter account that the tweets will come from, and that they’ll be expected to use mock values for “callback URI” and “website URL” because they aren’t using the Twitter app for authentication purposes, and that they’ll have to complete a step one time to provision their bot that normally users would do each time they sign up to use your app (the authorize step).
Only then will they get all four of the long lived credentials (the consumer key, consumer secret, token, and token secret) that they need to make requests to the Twitter API in an automated way.
EDIT:
Woops, just realized I posted the creds in the latest edit. Too late to redact them, so I just regenerated a new API Key and Secret in the portal. I’m assuming that automatically invalidates all access tokens and secrets generated using the client key.
…yep, looks like it did:
#!/bin/bash
curl -X POST \
https://api.twitter.com/2/tweets \
-H 'Authorization: OAuth oauth_consumer_key="ZR39xkPHqNsjlSX77gZtSGCcH", oauth_nonce="O70pkmQmoGcA4IlsemFvRkqLwq1JcehW", oauth_signature="6X7xcqP0rjMi3k5iJPzkq9%2B0Nlw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1641104950", oauth_token="1477507964298739712-VFdoDZug9ifkvA9GX89rEqoywZnblu", oauth_version="1.0"' \
-H 'Content-Type: application/json' \
-d '{"text": "Hello World 2!"}'
{
"title": "Unauthorized",
"type": "about:blank",
"status": 401,
"detail": "Unauthorized"
}