mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
refactor: Enhance Supabase integration and Twitter OAuth handling
- Updated `store.py` to improve state token management by adding PKCE support and simplifying code challenge generation. - Modified environment variable names in `.env.example` for consistency. - Removed unnecessary `is_multi_select` attributes from various Twitter-related input schemas to streamline the code. - Cleaned up exception handling in Twitter list management and tweet management blocks by removing redundant error logging. - Removed debug print statements from various components to clean up the codebase. - Fixed a minor error message in the Twitter OAuth handler for clarity.
This commit is contained in:
@@ -59,8 +59,8 @@ GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
|
||||
# Twitter/X OAuth App server credentials - https://developer.x.com/en/products/x-api
|
||||
Twitter_CLIENT_ID=
|
||||
Twitter_CLIENT_SECRET=
|
||||
TWITTER_CLIENT_ID=
|
||||
TWITTER_CLIENT_SECRET=
|
||||
|
||||
|
||||
## ===== OPTIONAL API KEYS ===== ##
|
||||
|
||||
@@ -258,7 +258,6 @@ class TweetExpansionInputs(BlockSchema):
|
||||
enum=TweetExpansions,
|
||||
placeholder="Pick the extra information you want to see",
|
||||
default=[],
|
||||
is_multi_select=True,
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
@@ -267,7 +266,6 @@ class TweetExpansionInputs(BlockSchema):
|
||||
enum=TweetMediaFields,
|
||||
placeholder="Choose what media details you want to see",
|
||||
default=[],
|
||||
is_multi_select=True,
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
@@ -276,7 +274,6 @@ class TweetExpansionInputs(BlockSchema):
|
||||
placeholder="Choose what location details you want to see",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetPlaceFields,
|
||||
)
|
||||
|
||||
@@ -285,7 +282,6 @@ class TweetExpansionInputs(BlockSchema):
|
||||
placeholder="Choose what poll details you want to see",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetPollFields,
|
||||
)
|
||||
|
||||
@@ -294,7 +290,6 @@ class TweetExpansionInputs(BlockSchema):
|
||||
placeholder="Choose what tweet details you want to see",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetFields,
|
||||
)
|
||||
|
||||
@@ -303,7 +298,6 @@ class TweetExpansionInputs(BlockSchema):
|
||||
placeholder="Choose what user details you want to see",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetUserFields,
|
||||
)
|
||||
|
||||
@@ -314,7 +308,6 @@ class DMEventExpansionInputs(BlockSchema):
|
||||
enum=DMEventExpansion,
|
||||
placeholder="Enter expansions",
|
||||
default=[],
|
||||
is_multi_select=True,
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
@@ -323,7 +316,6 @@ class DMEventExpansionInputs(BlockSchema):
|
||||
placeholder="Enter event types",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=DMEventType,
|
||||
)
|
||||
|
||||
@@ -332,7 +324,6 @@ class DMEventExpansionInputs(BlockSchema):
|
||||
placeholder="Enter media fields",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=DMMediaField,
|
||||
)
|
||||
|
||||
@@ -341,7 +332,6 @@ class DMEventExpansionInputs(BlockSchema):
|
||||
placeholder="Enter tweet fields",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=DMTweetField,
|
||||
)
|
||||
|
||||
@@ -350,7 +340,6 @@ class DMEventExpansionInputs(BlockSchema):
|
||||
placeholder="Enter user fields",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetUserFields,
|
||||
)
|
||||
|
||||
@@ -361,7 +350,6 @@ class UserExpansionInputs(BlockSchema):
|
||||
enum=UserExpansions,
|
||||
placeholder="Select extra user information to include",
|
||||
default=[],
|
||||
is_multi_select=True,
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
@@ -370,7 +358,6 @@ class UserExpansionInputs(BlockSchema):
|
||||
placeholder="Choose what details to see in pinned tweets",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetFields,
|
||||
)
|
||||
|
||||
@@ -379,7 +366,6 @@ class UserExpansionInputs(BlockSchema):
|
||||
placeholder="Choose what user details you want to see",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetUserFields,
|
||||
)
|
||||
|
||||
@@ -390,7 +376,6 @@ class SpaceExpansionInputs(BlockSchema):
|
||||
enum=SpaceExpansions,
|
||||
placeholder="Pick what extra information you want to see about the Space",
|
||||
default=[],
|
||||
is_multi_select=True,
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
@@ -399,7 +384,6 @@ class SpaceExpansionInputs(BlockSchema):
|
||||
placeholder="Choose what Space information you want to get",
|
||||
default=[SpaceFields.title_, SpaceFields.host_ids],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=SpaceFields,
|
||||
)
|
||||
|
||||
@@ -408,7 +392,6 @@ class SpaceExpansionInputs(BlockSchema):
|
||||
placeholder="Pick what details you want to see about the users",
|
||||
default=[],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetUserFields,
|
||||
)
|
||||
|
||||
@@ -419,7 +402,6 @@ class ListExpansionInputs(BlockSchema):
|
||||
enum=ListExpansions,
|
||||
placeholder="Pick what extra list information you want to see",
|
||||
default=[ListExpansions.owner_id],
|
||||
is_multi_select=True,
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
@@ -428,7 +410,6 @@ class ListExpansionInputs(BlockSchema):
|
||||
placeholder="Select what details you want to see about list owners",
|
||||
default=[TweetUserFields.id, TweetUserFields.username],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=TweetUserFields,
|
||||
)
|
||||
|
||||
@@ -437,7 +418,6 @@ class ListExpansionInputs(BlockSchema):
|
||||
placeholder="Pick what list details you want to see",
|
||||
default=[ListFields.owner_id],
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum=ListFields,
|
||||
)
|
||||
|
||||
|
||||
@@ -87,8 +87,7 @@ class TwitterRemoveListMemberBlock(Block):
|
||||
return True
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
@@ -163,8 +162,7 @@ class TwitterAddListMemberBlock(Block):
|
||||
return True
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
|
||||
@@ -60,8 +60,7 @@ class TwitterDeleteListBlock(Block):
|
||||
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
@@ -151,8 +150,7 @@ class TwitterUpdateListBlock(Block):
|
||||
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
@@ -256,8 +254,7 @@ class TwitterCreateListBlock(Block):
|
||||
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
|
||||
@@ -72,8 +72,7 @@ class TwitterUnpinListBlock(Block):
|
||||
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
@@ -137,8 +136,7 @@ class TwitterPinListBlock(Block):
|
||||
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
|
||||
@@ -147,8 +147,6 @@ class TwitterGetSpacesBlock(Block):
|
||||
.build()
|
||||
)
|
||||
|
||||
print(" before space response")
|
||||
|
||||
response = cast(Response, client.get_spaces(**params))
|
||||
|
||||
ids = []
|
||||
|
||||
@@ -211,8 +211,7 @@ class TwitterPostTweetBlock(Block):
|
||||
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
@@ -294,8 +293,7 @@ class TwitterDeleteTweetBlock(Block):
|
||||
return True
|
||||
except tweepy.TweepyException:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {str(e)}")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def run(
|
||||
|
||||
@@ -56,7 +56,7 @@ class TwitterGetQuoteTweetsBlock(Block):
|
||||
description="Types of tweets to exclude",
|
||||
required=False,
|
||||
advanced=True,
|
||||
is_multi_select=True,
|
||||
enum = TweetExcludes,
|
||||
default=[],
|
||||
)
|
||||
|
||||
|
||||
@@ -139,8 +139,6 @@ class TwitterGetUserBlock(Block):
|
||||
.build()
|
||||
)
|
||||
|
||||
print("params : ", params)
|
||||
|
||||
response = cast(Response, client.get_user(**params))
|
||||
|
||||
username = ""
|
||||
|
||||
@@ -136,7 +136,6 @@ def SchemaField(
|
||||
placeholder: Optional[str] = None,
|
||||
advanced: Optional[bool] = False,
|
||||
secret: bool = False,
|
||||
is_multi_select: bool = False,
|
||||
exclude: bool = False,
|
||||
hidden: Optional[bool] = None,
|
||||
depends_on: list[str] | None = None,
|
||||
|
||||
@@ -212,13 +212,11 @@ class IntegrationCredentialsStore:
|
||||
]
|
||||
self._set_user_integration_creds(user_id, filtered_credentials)
|
||||
|
||||
def store_state_token(self, user_id: str, provider: str, scopes: list[str]) -> tuple[str, str]:
|
||||
def store_state_token(self, user_id: str, provider: str, scopes: list[str], use_pkce: bool = False) -> tuple[str, str]:
|
||||
token = secrets.token_urlsafe(32)
|
||||
expires_at = datetime.now(timezone.utc) + timedelta(minutes=10)
|
||||
|
||||
code_verifier = self._generate_code_verifier(128)
|
||||
code_challenge = self._generate_code_challenge(code_verifier)
|
||||
print("login code_verifier: ", code_verifier)
|
||||
(code_challenge, code_verifier) = self._generate_code_challenge()
|
||||
|
||||
state = OAuthState(
|
||||
token=token,
|
||||
@@ -241,74 +239,17 @@ class IntegrationCredentialsStore:
|
||||
|
||||
return token, code_challenge
|
||||
|
||||
def _generate_code_verifier(self, length: int) -> str:
|
||||
"""
|
||||
Generate a secure random code verifier of specified length.
|
||||
"""
|
||||
return secrets.token_urlsafe(length)
|
||||
|
||||
def _generate_code_challenge(self, code_verifier: str, method: str = "S256") -> str:
|
||||
def _generate_code_challenge(self) -> tuple[str, str]:
|
||||
"""
|
||||
Generate code challenge using SHA256 from the code verifier.
|
||||
Currently only SHA256 is supported.(In future if we want to support more methods we can add them here)
|
||||
"""
|
||||
if method != "S256":
|
||||
raise ValueError(f"Unsupported code challenge method: {method}")
|
||||
|
||||
code_verifier = secrets.token_urlsafe(128)
|
||||
sha256_hash = hashlib.sha256(code_verifier.encode('utf-8')).digest()
|
||||
code_challenge = base64.urlsafe_b64encode(sha256_hash).decode('utf-8')
|
||||
return code_challenge.replace('=', '')
|
||||
return code_challenge.replace('=', ''), code_verifier
|
||||
|
||||
def get_any_valid_scopes_from_state_token(
|
||||
self, user_id: str, token: str, provider: str
|
||||
) -> list[str]:
|
||||
"""
|
||||
Get the valid scopes from the OAuth state token. This will return any valid scopes
|
||||
from any OAuth state token for the given provider. If no valid scopes are found,
|
||||
an empty list is returned. DO NOT RELY ON THIS TOKEN TO AUTHENTICATE A USER, AS IT
|
||||
IS TO CHECK IF THE USER HAS GIVEN PERMISSIONS TO THE APPLICATION BEFORE EXCHANGING
|
||||
THE CODE FOR TOKENS.
|
||||
"""
|
||||
user_integrations = self._get_user_integrations(user_id)
|
||||
oauth_states = user_integrations.oauth_states
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
valid_state = next(
|
||||
(
|
||||
state
|
||||
for state in oauth_states
|
||||
if state.token == token
|
||||
and state.provider == provider
|
||||
and state.expires_at > now.timestamp()
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if valid_state:
|
||||
return valid_state.scopes
|
||||
|
||||
return []
|
||||
|
||||
def _get_code_verifier(self, user_id: str, provider: str, token : str) -> Optional[str]:
|
||||
user_integrations = self._get_user_integrations(user_id)
|
||||
oauth_states = user_integrations.oauth_states
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
valid_state = next(
|
||||
(
|
||||
state
|
||||
for state in oauth_states
|
||||
if state.token == token
|
||||
and state.provider == provider
|
||||
and state.expires_at > now.timestamp()
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if valid_state:
|
||||
return valid_state.code_verifier
|
||||
|
||||
|
||||
def verify_state_token(self, user_id: str, token: str, provider: str) -> bool:
|
||||
def verify_state_token(self, user_id: str, token: str, provider: str) -> Optional[OAuthState]:
|
||||
with self.locked_user_integrations(user_id):
|
||||
user_integrations = self._get_user_integrations(user_id)
|
||||
oauth_states = user_integrations.oauth_states
|
||||
@@ -330,9 +271,9 @@ class IntegrationCredentialsStore:
|
||||
oauth_states.remove(valid_state)
|
||||
user_integrations.oauth_states = oauth_states
|
||||
self.db_manager.update_user_integrations(user_id, user_integrations)
|
||||
return True
|
||||
return valid_state
|
||||
|
||||
return False
|
||||
return None
|
||||
|
||||
def _set_user_integration_creds(
|
||||
self, user_id: str, credentials: list[Credentials]
|
||||
|
||||
@@ -48,7 +48,7 @@ class TwitterOAuthHandler(BaseOAuthHandler):
|
||||
# scopes = self.handle_default_scopes(scopes)
|
||||
|
||||
if code_challenge is None:
|
||||
raise ValueError("code_verifier is required for Twitter OAuth")
|
||||
raise ValueError("code_challenge is required for Twitter OAuth")
|
||||
|
||||
params = {
|
||||
"response_type": "code",
|
||||
|
||||
@@ -91,24 +91,20 @@ def callback(
|
||||
) -> CredentialsMetaResponse:
|
||||
logger.debug(f"Received OAuth callback for provider: {provider}")
|
||||
handler = _get_provider_oauth_handler(request, provider)
|
||||
code_verifier = creds_manager.store._get_code_verifier(
|
||||
user_id, provider, state_token
|
||||
)
|
||||
|
||||
# Verify the state token
|
||||
if not creds_manager.store.verify_state_token(user_id, state_token, provider):
|
||||
valid_state = creds_manager.store.verify_state_token(user_id, state_token, provider)
|
||||
|
||||
if not valid_state:
|
||||
logger.warning(f"Invalid or expired state token for user {user_id}")
|
||||
raise HTTPException(status_code=400, detail="Invalid or expired state token")
|
||||
|
||||
try:
|
||||
scopes = creds_manager.store.get_any_valid_scopes_from_state_token(
|
||||
user_id, state_token, provider
|
||||
)
|
||||
scopes = valid_state.scopes
|
||||
logger.debug(f"Retrieved scopes from state token: {scopes}")
|
||||
|
||||
scopes = handler.handle_default_scopes(scopes)
|
||||
|
||||
credentials = handler.exchange_code_for_tokens(code, scopes, code_verifier)
|
||||
credentials = handler.exchange_code_for_tokens(code, scopes, valid_state.code_verifier)
|
||||
|
||||
logger.debug(f"Received credentials with final scopes: {credentials.scopes}")
|
||||
|
||||
|
||||
@@ -297,9 +297,6 @@ export function CustomNode({
|
||||
const newValues = JSON.parse(JSON.stringify(data.hardcodedValues));
|
||||
let current = newValues;
|
||||
|
||||
console.log("Keys: ", keys);
|
||||
console.log("Value: ", newValues);
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
const { key: currentKey, index } = keys[i];
|
||||
if (index !== undefined) {
|
||||
|
||||
@@ -862,10 +862,7 @@ const NodeArrayInput: FC<{
|
||||
if (!entries || !Array.isArray(entries)) entries = [];
|
||||
|
||||
const isMultiSelectEnum =
|
||||
schema.items &&
|
||||
isStringSubSchema(schema.items) &&
|
||||
schema.items.enum &&
|
||||
schema.isMultiSelect;
|
||||
schema.items && isStringSubSchema(schema.items) && schema.items.enum;
|
||||
|
||||
if (isMultiSelectEnum) {
|
||||
return (
|
||||
|
||||
@@ -76,7 +76,6 @@ export type BlockIOKVSubSchema = BlockIOSubSchemaMeta & {
|
||||
export type BlockIOArraySubSchema = BlockIOSubSchemaMeta & {
|
||||
type: "array";
|
||||
items?: BlockIOSimpleTypeSubSchema;
|
||||
isMultiSelect?: boolean;
|
||||
default?: Array<string>;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user