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.
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.
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.
Also, if anyone has a better working suggestion? Either with Guzzle, Laravel Http, Laravel Socialite, or any other dependencies, I would appreciate. Thanks!
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'
]
);