mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
feat(oauth): added x oauth, needs to be fixed bc twitter doesn't respond with user's email
This commit is contained in:
@@ -12,7 +12,7 @@ interface GoogleIdToken {
|
||||
}
|
||||
|
||||
// Valid OAuth providers
|
||||
const VALID_PROVIDERS = ['google', 'github', 'twitter']
|
||||
const VALID_PROVIDERS = ['google', 'github', 'x']
|
||||
|
||||
/**
|
||||
* Get all OAuth connections for the current user
|
||||
|
||||
@@ -88,10 +88,10 @@ export async function POST(request: NextRequest) {
|
||||
clientId = process.env.GITHUB_CLIENT_ID
|
||||
clientSecret = process.env.GITHUB_CLIENT_SECRET
|
||||
break
|
||||
case 'twitter':
|
||||
tokenEndpoint = 'https://api.twitter.com/2/oauth2/token'
|
||||
clientId = process.env.TWITTER_CLIENT_ID
|
||||
clientSecret = process.env.TWITTER_CLIENT_SECRET
|
||||
case 'x':
|
||||
tokenEndpoint = 'https://api.x.com/2/oauth2/token'
|
||||
clientId = process.env.X_CLIENT_ID
|
||||
clientSecret = process.env.X_CLIENT_SECRET
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported provider: ${provider}`)
|
||||
|
||||
@@ -43,9 +43,10 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
repo: 'Access your repositories',
|
||||
workflow: 'Manage repository workflows',
|
||||
'user:email': 'Access your email address',
|
||||
'tweet.read': 'Read your tweets',
|
||||
'tweet.write': 'Create tweets on your behalf',
|
||||
'tweet.read': 'Read your tweets and timeline',
|
||||
'tweet.write': 'Post tweets on your behalf',
|
||||
'users.read': 'Read your profile information',
|
||||
'offline.access': 'Access your account when you are not using the application',
|
||||
}
|
||||
|
||||
// Convert OAuth scope to user-friendly description
|
||||
|
||||
+36
-24
@@ -38,11 +38,6 @@ export const auth = betterAuth({
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
],
|
||||
},
|
||||
// twitter: {
|
||||
// clientId: process.env.TWITTER_CLIENT_ID as string,
|
||||
// clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
|
||||
// scopes: ['tweet.read', 'users.read'],
|
||||
// },
|
||||
},
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
@@ -193,25 +188,42 @@ export const auth = betterAuth({
|
||||
pkce: true,
|
||||
},
|
||||
|
||||
// Twitter providers
|
||||
// {
|
||||
// providerId: 'twitter-read',
|
||||
// clientId: process.env.TWITTER_CLIENT_ID as string,
|
||||
// clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
|
||||
// authorizationUrl: 'https://twitter.com/i/oauth2/authorize',
|
||||
// tokenUrl: 'https://api.twitter.com/2/oauth2/token',
|
||||
// userInfoUrl: 'https://api.twitter.com/2/users/me',
|
||||
// scopes: ['tweet.read', 'users.read'],
|
||||
// },
|
||||
// {
|
||||
// providerId: 'twitter-write',
|
||||
// clientId: process.env.TWITTER_CLIENT_ID as string,
|
||||
// clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
|
||||
// authorizationUrl: 'https://twitter.com/i/oauth2/authorize',
|
||||
// tokenUrl: 'https://api.twitter.com/2/oauth2/token',
|
||||
// userInfoUrl: 'https://api.twitter.com/2/users/me',
|
||||
// scopes: ['tweet.read', 'tweet.write', 'users.read', 'offline.access'],
|
||||
// },
|
||||
// X provider
|
||||
{
|
||||
providerId: 'x',
|
||||
clientId: process.env.X_CLIENT_ID as string,
|
||||
clientSecret: process.env.X_CLIENT_SECRET as string,
|
||||
authorizationUrl: 'https://x.com/i/oauth2/authorize',
|
||||
tokenUrl: 'https://api.x.com/2/oauth2/token',
|
||||
userInfoUrl: 'https://api.x.com/2/users/me',
|
||||
scopes: ['tweet.read', 'tweet.write', 'users.read', 'offline.access'],
|
||||
pkce: true,
|
||||
redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/x`,
|
||||
getUserInfo: async (tokens) => {
|
||||
const response = await fetch(
|
||||
'https://api.x.com/2/users/me?user.fields=profile_image_url',
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const profile = await response.json()
|
||||
|
||||
const now = new Date()
|
||||
|
||||
return {
|
||||
id: profile.data.id,
|
||||
name: profile.data.name,
|
||||
email: profile.data.username || null, // Use username as email
|
||||
image: profile.data.profile_image_url,
|
||||
emailVerified: profile.data.verified || false,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
||||
+4
-2
@@ -135,11 +135,11 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
x: {
|
||||
id: 'x',
|
||||
name: 'X',
|
||||
description: 'Post tweets and interact with the X API.',
|
||||
description: 'Read and post tweets on X (formerly Twitter).',
|
||||
providerId: 'x',
|
||||
icon: (props) => xIcon(props),
|
||||
baseProviderIcon: (props) => xIcon(props),
|
||||
scopes: [],
|
||||
scopes: ['tweet.read', 'tweet.write', 'users.read', 'offline.access'],
|
||||
},
|
||||
},
|
||||
defaultService: 'x',
|
||||
@@ -207,6 +207,8 @@ export function getServiceIdFromScopes(provider: OAuthProvider, scopes: string[]
|
||||
}
|
||||
} else if (provider === 'supabase') {
|
||||
return 'supabase'
|
||||
} else if (provider === 'x') {
|
||||
return 'x'
|
||||
}
|
||||
|
||||
return providerConfig.defaultService
|
||||
|
||||
Generated
+106
-2135
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -37,7 +37,7 @@ export const readTool: ToolConfig<XReadParams, XReadResponse> = {
|
||||
'attachments.poll_ids',
|
||||
].join(',')
|
||||
|
||||
return `https://api.twitter.com/2/tweets/${params.tweetId}?expansions=${expansions}`
|
||||
return `https://api.x.com/2/tweets/${params.tweetId}?expansions=${expansions}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ export const searchTool: ToolConfig<XSearchParams, XSearchResponse> = {
|
||||
if (params.endTime) queryParams.append('end_time', params.endTime)
|
||||
if (params.sortOrder) queryParams.append('sort_order', params.sortOrder)
|
||||
|
||||
return `https://api.twitter.com/2/tweets/search/recent?${queryParams.toString()}`
|
||||
return `https://api.x.com/2/tweets/search/recent?${queryParams.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ export const userTool: ToolConfig<XUserParams, XUserResponse> = {
|
||||
','
|
||||
)
|
||||
|
||||
return `https://api.twitter.com/2/users/by/username/${username}?user.fields=${userFields}`
|
||||
return `https://api.x.com/2/users/by/username/${username}?user.fields=${userFields}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
@@ -83,7 +83,7 @@ export const userTool: ToolConfig<XUserParams, XUserResponse> = {
|
||||
// Fetch recent tweets if requested
|
||||
if (includeRecentTweets && apiKey) {
|
||||
const tweetsResponse = await fetch(
|
||||
`https://api.twitter.com/2/users/${user.id}/tweets?max_results=10&tweet.fields=created_at,conversation_id,in_reply_to_user_id,attachments`,
|
||||
`https://api.x.com/2/users/${user.id}/tweets?max_results=10&tweet.fields=created_at,conversation_id,in_reply_to_user_id,attachments`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ export const writeTool: ToolConfig<XWriteParams, XWriteResponse> = {
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.twitter.com/2/tweets',
|
||||
url: 'https://api.x.com/2/tweets',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
|
||||
Reference in New Issue
Block a user