phplaraveltwittertwitter-oauth

Laravel - Twitter/X API Integration For Creating New Posts


I'm in the middle of a proof-of-concept work, related to Twitter/X API integration and automation. This step is for basic exploration, so the code is not production ready.

Context:

Twitter/X Developer account - User Authentication Settings:

My code is almost working perfectly. This is what I have set up in Laravel.

composer.json

"atymic/twitter": "^3.0",

.env

# Consumer keys:
TWITTER_CONSUMER_KEY=123
TWITTER_CONSUMER_SECRET=321

# Authentication Token (not OAuth 2.0)
TWITTER_ACCESS_TOKEN=456
TWITTER_ACCESS_TOKEN_SECRET=654
TWITTER_API_VERSION=1.1

config\twitter.php

return [
    'debug' => env('APP_DEBUG', false),

    'api_url' => 'api.twitter.com',
    'upload_url' => 'upload.twitter.com',
    'api_version' => env('TWITTER_API_VERSION', '1.1'),

    'consumer_key' => env('TWITTER_CONSUMER_KEY'),
    'consumer_secret' => env('TWITTER_CONSUMER_SECRET'),
    'access_token' => env('TWITTER_ACCESS_TOKEN'),
    'access_token_secret' => env('TWITTER_ACCESS_TOKEN_SECRET'),
    'bearer_token' => env('TWITTER_BEARER_TOKEN'),

    'authenticate_url' => 'https://api.twitter.com/oauth/authenticate',
    'access_token_url' => 'https://api.twitter.com/oauth/access_token',
    'request_token_url' => 'https://api.twitter.com/oauth/request_token',
];

app\Http\Controllers\TwitterController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Atymic\Twitter\Facade\Twitter;
use Atymic\Twitter\Twitter as TwitterContract;
use Illuminate\Support\Facades\Http;

class TwitterController extends Controller
{
    private $twitter;

    public function __construct(TwitterContract $twitter)
    {
        $this->twitter = $twitter;
    }

    public function redirectToTwitter()
    {
        $authCallback = 'http://127.0.0.1:8000/auth/twitter/callback';

        $response = $this->twitter->usingCredentials(
            config('twitter.access_token'),
            config('twitter.access_token_secret'),
            config('twitter.consumer_key'),
            config('twitter.consumer_secret')
        )
        ->getRequestToken($authCallback);

        $url = 'https://api.twitter.com/oauth/authenticate?oauth_token=' . $response['oauth_token'];


        session(['oauth_token' => $response['oauth_token']]);
        session(['oauth_token_secret' => $response['oauth_token_secret']]);


        return redirect($url);
    }


    public function handleProviderCallback(Request $request)
    {
        $oauthToken = $request->query('oauth_token');
        $oauthVerifier = $request->query('oauth_verifier');

        if (!$oauthToken || !$oauthVerifier) {
            return redirect()->route('twitter.error')->withErrors('Invalid OAuth response from Twitter');
        }

        // Ensure the oauth_token matches the one stored in the session.
        if ($oauthToken !== session('oauth_token')) {
            // TODO: Log the error or handle it as per your application's needs
        }

        try {

            $tokenCredentials = $this->twitter->usingCredentials(session('oauth_token'), session('oauth_token_secret'))
            ->getAccessToken($oauthVerifier);

            session([
                'twitter_access_token' => $tokenCredentials['oauth_token'],
                'twitter_token_secret' => $tokenCredentials['oauth_token_secret'],
            ]);

            // Post a tweet or perform other actions with the authenticated user's token credentials.
            if ($this->postTweet($request)) {
                // The tweet was successfully posted
                return response()->json(['status' => 'OAuth Success'], 200);
            } else {
                // The tweet was not posted
                return response()->json(['status' => 'OAuth Success, but error posting message'], 200);
            }


        } catch (\Exception $e) {
            // Handle errors, such as logging or displaying a message
            return redirect()->route('twitter.error')->withErrors('Failed to obtain access tokens from Twitter');

        }
    }

    public function postTweet(Request $request)
    {
        $status = $request->input('status', 'This is a tweet posted from my Laravel application using API v2.');

        try {
            $twitter = $this->twitter->usingCredentials(
                config('twitter.access_token'),
                config('twitter.access_token_secret'),
                config('twitter.consumer_key'),
                config('twitter.consumer_secret')
            );

            // Post the tweet
            $response = $twitter->postTweet(['status' => $status]);

            if (isset($response['data']) && isset($response['data']['id'])) {
                // return response()->json(['status' => 'Message posted', 'tweet_id' => $response['data']['id']], 200);
                return true;
            } else {
                // return response()->json(['status' => 'Error posting message', 'error' => $response], 500);
                return false;
            }
        } catch (\Exception $e) {
            // return response()->json(['status' => 'Exception caught', 'error' => $e->getMessage()], 500);
            return false;
        }
    }
}

routes\web.php

use App\Http\Controllers\TwitterController;

Route::get('auth/twitter', [TwitterController::class, 'redirectToTwitter'])->name('twitter.login');
Route::get('auth/twitter/callback', [TwitterController::class, 'handleProviderCallback'])->name('twitter.callback');
Route::get('auth/twitter/post', [TwitterController::class, 'postTweet'])->name('twitter.post');

The methods redirectToTwitter and handleProviderCallback are working fine. I can authenticate and the callback works perfectly.

Problem

The problem is with postTweet, more specifically, this line:

$response = $twitter->postTweet(['status' => $status]);

When this line executes, it returns me this error message:

[453] You currently have access to a subset of Twitter API v2 endpoints and limited v1.1 endpoints (e.g. media post, oauth) only. If you need access to this endpoint, you may need a different access level. You can learn more here: https://developer.twitter.com/en/portal/product

Initially, I suspected it were the API version I was using TWITTER_API_VERSION=1.1 as I read somewhere that the API version 1 was being deprecated. However, I switched that to TWITTER_API_VERSION=2 and did an isolated test with the auth/twitter/post route, but the problem persisted.

Other Hypophysis:

Also, if anyone has a better working suggestion? Either with Guzzle, Laravel Http, Laravel Socialite, or any other dependencies, I would appreciate. Thanks!


Solution

  • I was just implementing Atymic Twitter with Laravel 10 today.

    Here's what I did that got it to work:

    Additionally, according to the issues post, postTweet is only available for Twitter API v1 however the code provided by Github user @reliq is working for me: https://github.com/atymic/twitter/issues/399#issuecomment-1254272114

    use Atymic\Twitter\Contract\Http\Client;
    use Atymic\Twitter\Facade\Twitter;
    
    ...
    
    $querier = Twitter::forApiV2()->getQuerier();
    $result = $querier->post(
        'tweets', 
        [
            Client::KEY_REQUEST_FORMAT => Client::REQUEST_FORMAT_JSON,
            'text' => 'my tweet'
        ]
    );