From d42ef421f980885d668c2fa06519f9a203b4235f Mon Sep 17 00:00:00 2001 From: Elliot Braem Date: Fri, 10 Jan 2025 11:38:03 -0500 Subject: [PATCH] oauth and oauth2 --- .env.example | 8 ++++++- src/services/twitter.js | 52 ++++++++++++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index 01bac4a..3283ce2 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,12 @@ -# Twitter OAuth 2.0 credentials +# Twitter OAuth 2.0 credentials (for tweet.read, tweet.write) TWITTER_CLIENT_ID=your_oauth2_client_id TWITTER_CLIENT_SECRET=your_oauth2_client_secret +# Twitter OAuth 1.0a credentials (for user context endpoints) +TWITTER_API_KEY=your_oauth1_api_key +TWITTER_API_SECRET=your_oauth1_api_secret +TWITTER_ACCESS_TOKEN=your_oauth1_access_token +TWITTER_ACCESS_SECRET=your_oauth1_access_secret + # Base URL for OAuth callback NEXT_PUBLIC_BASE_URL=http://localhost:3000 diff --git a/src/services/twitter.js b/src/services/twitter.js index e31914e..a045285 100644 --- a/src/services/twitter.js +++ b/src/services/twitter.js @@ -4,26 +4,44 @@ import { TwitterApi } from "twitter-api-v2"; // which manages API credentials and handles OAuth communciation with Twitter export class TwitterService { - constructor(clientId, clientSecret) { - if (!clientId || !clientSecret) { + constructor(credentials) { + if (!credentials.clientId || !credentials.clientSecret) { throw new Error("Twitter OAuth 2.0 credentials are required"); } - this.client = new TwitterApi({ - clientId: clientId, - clientSecret: clientSecret, + + // OAuth 2.0 client for tweet operations + this.oauth2Client = new TwitterApi({ + clientId: credentials.clientId, + clientSecret: credentials.clientSecret, }); + + // OAuth 1.0a client for user operations if credentials are provided + if (credentials.apiKey && credentials.apiSecret && credentials.accessToken && credentials.accessSecret) { + this.oauth1Client = new TwitterApi({ + appKey: credentials.apiKey, + appSecret: credentials.apiSecret, + accessToken: credentials.accessToken, + accessSecret: credentials.accessSecret, + }); + } } static async initialize() { - const clientId = process.env.TWITTER_CLIENT_ID; - const clientSecret = process.env.TWITTER_CLIENT_SECRET; + const credentials = { + clientId: process.env.TWITTER_CLIENT_ID, + clientSecret: process.env.TWITTER_CLIENT_SECRET, + apiKey: process.env.TWITTER_API_KEY, + apiSecret: process.env.TWITTER_API_SECRET, + accessToken: process.env.TWITTER_ACCESS_TOKEN, + accessSecret: process.env.TWITTER_ACCESS_SECRET, + }; - return new TwitterService(clientId, clientSecret); + return new TwitterService(credentials); } async getAuthLink(callbackUrl) { // Use OAuth 2.0 with PKCE for more granular scope control - const { url, codeVerifier, state } = this.client.generateOAuth2AuthLink( + const { url, codeVerifier, state } = this.oauth2Client.generateOAuth2AuthLink( callbackUrl, { scope: ["tweet.read", "tweet.write", "users.read"] }, ); @@ -31,7 +49,7 @@ export class TwitterService { } async handleCallback(code, codeVerifier, state) { - return this.client.loginWithOAuth2({ + return this.oauth2Client.loginWithOAuth2({ code, codeVerifier, redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/api/twitter/callback`, @@ -39,6 +57,7 @@ export class TwitterService { } async tweet(accessToken, posts) { + // Create OAuth 2.0 client with user access token for tweet operations const userClient = new TwitterApi(accessToken); // Handle array of post objects @@ -69,9 +88,16 @@ export class TwitterService { } async getUserInfo(accessToken) { - const userClient = new TwitterApi(accessToken); + if (!this.oauth1Client) { + throw new Error("OAuth 1.0a credentials are required for user operations"); + } - const me = await userClient.v2.me(); - return me.data; + try { + const me = await this.oauth1Client.v2.me(); + return me.data; + } catch (error) { + console.error("Failed to fetch user info:", error); + throw error; + } } }