twitter-lite icon indicating copy to clipboard operation
twitter-lite copied to clipboard

OAuth 2 error for POST /2/tweets FIXED

Open charlesr1971 opened this issue 4 years ago • 16 comments

I am using the latest version of twitter-lite

I thought we were using oAuth 1.0a, but I get this error:

{
  _headers: Headers {
    [Symbol(map)]: [Object: null prototype] {
      'set-cookie': [Array],
      'content-type': [Array],
      'cache-control': [Array],
      'content-length': [Array],
      'x-response-time': [Array],
      'x-connection-hash': [Array],
      date: [Array],
      server: [Array],
      connection: [Array]
    }
  },
  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
}

Here is the code

async function createNewTwitterClient (subdomain, extension, response, twitterAppConfig, version) {
  const twitter = require("twitter-lite");
  return new twitter({
    subdomain,
    consumer_key: twitterAppConfig.consumerKey, // from Twitter.
    consumer_secret: twitterAppConfig.consumerSecret,
    extension, // true is the default (this must be set to false for v2 endpoints),
    access_token_key: twitterAppConfig.accessTokenKey, // from your User (oauth_token)
    access_token_secret: twitterAppConfig.accessTokenSecret, // from your User (oauth_token_secret)
    bearer_token: response ? response?.access_token : null,
    version
  });
}

const apiClient = await createNewTwitterClient('api', false, null, twitterAppConfig,'2');
const apiResponse = await apiClient.getBearerToken();
const twitterApiClient = await createNewTwitterClient('api', false, apiResponse, twitterAppConfig,'2');

twitterApiClient.post('tweets', { text: 'hello world' }).then(tweet => {
	// do something
}).catch();

In Postman, it works fine:

image

charlesr1971 avatar Dec 08 '21 12:12 charlesr1971

I have found the fix:

const JSON_ENDPOINTS = [
  'direct_messages/events/new',
  'direct_messages/welcome_messages/new',
  'direct_messages/welcome_messages/rules/new',
  'media/metadata/create',
  'collections/entries/curate',
  'tweets',
];

Oh. And I had to update my code as well:

Here is the code

async function createNewTwitterClient (subdomain, extension, response, twitterAppConfig, version) {
  const twitter = require("twitter-lite");
  return new twitter({
    subdomain,
    consumer_key: twitterAppConfig.consumerKey, // from Twitter.
    consumer_secret: twitterAppConfig.consumerSecret,
    extension, // true is the default (this must be set to false for v2 endpoints),
    access_token_key: twitterAppConfig.accessTokenKey, // from your User (oauth_token)
    access_token_secret: twitterAppConfig.accessTokenSecret, // from your User (oauth_token_secret)
    bearer_token: response ? response?.access_token : null,
    version
  });
}

const twitterApiClient = await createNewTwitterClient('api', false, null, twitterAppConfig,'2');

twitterApiClient.post('tweets', { text: 'hello world' }).then(tweet => {
	// do something
}).catch();

So, I removed the bearer token from the twitterAppConfig

Maybe, you should be allowed to send the bearer token, along with the access token, without losing the ability to POST. Currently, your code chooses which type of authentication to use, based on whether the bearer token is present. It might be better to choose which type of authentication to use, based on whether the bearer token is present and whether the access token is not present?

charlesr1971 avatar Dec 08 '21 18:12 charlesr1971

const client = new Twitter({
      version: "2",
      extension: false,
      consumer_key: process.env.TWITTER_CONSUMER_KEY,
      consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
      access_token_key: oauthToken,
      access_token_secret: oauthSecret,
    });
client.post('tweets', { text: 'hello world' }).then(tweet => {
      // do something
      console.log(tweet)
    }).catch((e) => console.log(e));

Response:

{
  _headers: Headers {
    [Symbol(map)]: [Object: null prototype] {
      date: [Array],
      server: [Array],
      'set-cookie': [Array],
      'api-version': [Array],
      'content-type': [Array],
      'cache-control': [Array],
      'content-length': [Array],
      'x-frame-options': [Array],
      'content-encoding': [Array],
      'x-xss-protection': [Array],
      'x-rate-limit-limit': [Array],
      'x-rate-limit-reset': [Array],
      'content-disposition': [Array],
      'x-content-type-options': [Array],
      'x-rate-limit-remaining': [Array],
      'strict-transport-security': [Array],
      'x-response-time': [Array],
      'x-connection-hash': [Array],
      connection: [Array]
    }
  },
  errors: [
    {
      parameters: {},
      message: 'Requests with bodies must have content-type of application/json.'
    }
  ],
  title: 'Invalid Request',
  detail: 'One or more parameters to your request was invalid.',
  type: 'https://api.twitter.com/2/problems/invalid-request'
}

any fix? @charlesr1971

ZainUrRehmanKhan avatar Feb 28 '22 07:02 ZainUrRehmanKhan

Try adding:

subdomain: 'api'

To the arguments of:

new Twitter({ subdomain: 'api' ... })

Argument object...

charlesr1971 avatar Feb 28 '22 07:02 charlesr1971

Still same response.

ZainUrRehmanKhan avatar Feb 28 '22 08:02 ZainUrRehmanKhan

Please try using this exact code:

`async function createNewTwitterClient (subdomain, extension, response, twitterAppConfig, version) { const twitter = require("twitter-lite"); return new twitter({ subdomain, consumer_key: twitterAppConfig.consumerKey, consumer_secret: twitterAppConfig.consumerSecret, extension, access_token_key: twitterAppConfig.accessTokenKey, access_token_secret: twitterAppConfig.accessTokenSecret, bearer_token: response ? response?.access_token : null, version }); }

const apiClient = await createNewTwitterClient('api', false, null, twitterAppConfig,'2'); const apiResponse = await apiClient.getBearerToken(); const twitterApiClient = await createNewTwitterClient('api', false, apiResponse, twitterAppConfig,'2');

twitterApiClient.post('tweets', { text: 'hello world' }).then(tweet => { }).catch();`

This works for me!

My twitterAppConfig is:

const twitterAppConfig = { consumerKey: APP_CONFIG.CONSUMER_KEY1, consumerSecret: APP_CONFIG.CONSUMER_SECRET1, accessTokenKey: APP_CONFIG.ACCESS_TOKEN1, accessTokenSecret: APP_CONFIG.ACCESS_TOKEN_SECRET1 };

charlesr1971 avatar Feb 28 '22 09:02 charlesr1971

Also:

https://github.com/draftbit/twitter-lite/pull/187

**This PR has not been merged yet. **

I had to change the code inside the Twitter-Lite for /tweets v2 to work!

charlesr1971 avatar Feb 28 '22 09:02 charlesr1971

God, this MarkDown Editor is terrible. You would think GitHub could sort this out? 😩

charlesr1971 avatar Feb 28 '22 09:02 charlesr1971

https://github.com/charlesr1971/twitter-lite/commit/d5e9ef19ac6f8c5807a2d78671e4de0c209dd5da

charlesr1971 avatar Feb 28 '22 09:02 charlesr1971

I'm still facing the same issue, I tried the exact same code you mentioned above. Is there any workaround or other package which I can use for now?

ZainUrRehmanKhan avatar Feb 28 '22 10:02 ZainUrRehmanKhan

why there is no content-type application/json?

ZainUrRehmanKhan avatar Feb 28 '22 10:02 ZainUrRehmanKhan

Yes. You need to use the twitter-lite version in my PR request above:

https://github.com/draftbit/twitter-lite/pull/187

Unfortunately, the owners of the twitter-lite repo, are not merging PR requests at the moment.

But, you can clone my repository and use that:

https://github.com/charlesr1971/twitter-lite/tree/%40charlesr1971

charlesr1971 avatar Feb 28 '22 10:02 charlesr1971

Here is the commit:

https://github.com/charlesr1971/twitter-lite/commit/d5e9ef19ac6f8c5807a2d78671e4de0c209dd5da

charlesr1971 avatar Feb 28 '22 10:02 charlesr1971

I tried using your repo for the package but It's also not working.

ZainUrRehmanKhan avatar Feb 28 '22 12:02 ZainUrRehmanKhan

All I know is that the current Twitter-lite V2 /tweets endpoint does NOT work.

My PR code updates, work with me.

I must say your content type error message, is a little strange.

charlesr1971 avatar Feb 28 '22 13:02 charlesr1971

Chiming in here to say that I'm also receiving the same content-type error for the /tweets endpoint.

 try {
    const client = new Twitter({
      subdomain: "api", // With or without this parameter, the error persists
      version: "2",
      extension: false,
      consumer_key,
      consumer_secret,
      access_token_key,
      access_token_secret,
    });

    const tweet = await client.post("tweets", {
      text: "Hello, World!",
    });

    res.send({ tweet });
  } catch (error) {
    res.send({ error });
  }

Reponds with:

{
  "error": {
    "_headers": {},
    "errors": [
      {
        "message": "Requests with bodies must have content-type of application/json."
      }
    ],
    "title": "Invalid Request",
    "detail": "One or more parameters to your request was invalid.",
    "type": "https://api.twitter.com/2/problems/invalid-request"
  }
}

@charlesr1971 I will take a look at your PR!

McCambley avatar Sep 19 '22 05:09 McCambley