Skip to content

Commit b77d45d

Browse files
Comprehensive OAUTH integration
1 parent c54eeee commit b77d45d

File tree

2 files changed

+86
-41
lines changed

2 files changed

+86
-41
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@
7070
# GOOGLE_API_KEY="Fill your API key here"
7171
# SEARCH_ENGINE_ID="Fill your API key here"
7272

73+
# Google OAUTH credentials (https://developers.google.com/identity/gsi/web/guides)
74+
#GOOGLE_CLIENT_ID="Fill your client_id here"
75+
#GOOGLE_CLIENT_SECRET="Fill your client_secret here"
76+
7377
# OpenWeatherMap API (https://home.openweathermap.org/users/sign_up)
7478
# OPENWEATHERMAP_API_KEY="Fill your API key here"
7579

camel/toolkits/gmail_toolkit.py

Lines changed: 82 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from camel.logger import get_logger
2525
from camel.toolkits import FunctionTool
2626
from camel.toolkits.base import BaseToolkit
27-
from camel.utils import MCPServer, api_keys_required
27+
from camel.utils import MCPServer
2828

2929
logger = get_logger(__name__)
3030

@@ -1049,57 +1049,98 @@ def _get_people_service(self):
10491049
except Exception as e:
10501050
raise ValueError(f"Failed to build People service: {e}") from e
10511051

1052-
@api_keys_required(
1053-
[
1054-
(None, "GOOGLE_CLIENT_ID"),
1055-
(None, "GOOGLE_CLIENT_SECRET"),
1056-
]
1057-
)
10581052
def _authenticate(self):
1059-
r"""Authenticate with Google APIs."""
1060-
client_id = os.environ.get('GOOGLE_CLIENT_ID')
1061-
client_secret = os.environ.get('GOOGLE_CLIENT_SECRET')
1062-
refresh_token = os.environ.get('GOOGLE_REFRESH_TOKEN')
1063-
token_uri = os.environ.get(
1064-
'GOOGLE_TOKEN_URI', 'https://oauth2.googleapis.com/token'
1065-
)
1053+
r"""Authenticate with Google APIs using OAuth2.
1054+
1055+
Automatically saves and loads credentials from
1056+
~/.camel/gmail_token.json to avoid repeated
1057+
browser logins.
1058+
"""
1059+
import json
1060+
from pathlib import Path
10661061

1062+
from dotenv import load_dotenv
10671063
from google.auth.transport.requests import Request
10681064
from google.oauth2.credentials import Credentials
10691065
from google_auth_oauthlib.flow import InstalledAppFlow
10701066

1071-
# For first-time authentication
1072-
if not refresh_token:
1073-
client_config = {
1074-
"installed": {
1075-
"client_id": client_id,
1076-
"client_secret": client_secret,
1077-
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
1078-
"token_uri": token_uri,
1079-
"redirect_uris": ["http://localhost"],
1080-
}
1081-
}
1067+
# Look for .env file in the project root (camel/)
1068+
env_file = Path(__file__).parent.parent.parent / '.env'
1069+
load_dotenv(env_file)
10821070

1083-
flow = InstalledAppFlow.from_client_config(client_config, SCOPES)
1084-
creds = flow.run_local_server(port=0)
1085-
return creds
1086-
else:
1087-
# If we have a refresh token, use it to get credentials
1088-
creds = Credentials(
1089-
None,
1090-
refresh_token=refresh_token,
1091-
token_uri=token_uri,
1092-
client_id=client_id,
1093-
client_secret=client_secret,
1094-
scopes=SCOPES,
1095-
)
1071+
client_id = os.environ.get('GOOGLE_CLIENT_ID')
1072+
client_secret = os.environ.get('GOOGLE_CLIENT_SECRET')
10961073

1097-
# Refresh token if expired
1098-
if creds.expired:
1099-
creds.refresh(Request())
1074+
token_file = Path.home() / '.camel' / 'gmail_token.json'
1075+
creds = None
1076+
1077+
# COMPONENT 1: Load saved credentials
1078+
if token_file.exists():
1079+
try:
1080+
with open(token_file, 'r') as f:
1081+
data = json.load(f)
1082+
creds = Credentials(
1083+
token=data.get('token'),
1084+
refresh_token=data.get('refresh_token'),
1085+
token_uri=data.get(
1086+
'token_uri', 'https://oauth2.googleapis.com/token'
1087+
),
1088+
client_id=client_id,
1089+
client_secret=client_secret,
1090+
scopes=SCOPES,
1091+
)
1092+
except Exception as e:
1093+
logger.warning(f"Failed to load saved token: {e}")
1094+
creds = None
11001095

1096+
# COMPONENT 2: Refresh if expired
1097+
if creds and creds.expired and creds.refresh_token:
1098+
try:
1099+
creds.refresh(Request())
1100+
logger.info("Access token refreshed")
1101+
return creds
1102+
except Exception as e:
1103+
logger.warning(f"Token refresh failed: {e}")
1104+
creds = None
1105+
1106+
# COMPONENT 3: Return if valid
1107+
if creds and creds.valid:
11011108
return creds
11021109

1110+
# COMPONENT 4: Browser OAuth (first-time or invalid credentials)
1111+
client_config = {
1112+
"installed": {
1113+
"client_id": client_id,
1114+
"client_secret": client_secret,
1115+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
1116+
"token_uri": "https://oauth2.googleapis.com/token",
1117+
"redirect_uris": ["http://localhost"],
1118+
}
1119+
}
1120+
1121+
flow = InstalledAppFlow.from_client_config(client_config, SCOPES)
1122+
creds = flow.run_local_server(port=0)
1123+
1124+
# Save new credentials
1125+
token_file.parent.mkdir(parents=True, exist_ok=True)
1126+
with open(token_file, 'w') as f:
1127+
json.dump(
1128+
{
1129+
'token': creds.token,
1130+
'refresh_token': creds.refresh_token,
1131+
'token_uri': creds.token_uri,
1132+
'scopes': creds.scopes,
1133+
},
1134+
f,
1135+
)
1136+
try:
1137+
os.chmod(token_file, 0o600)
1138+
except Exception:
1139+
pass
1140+
logger.info(f"Credentials saved to {token_file}")
1141+
1142+
return creds
1143+
11031144
def _create_message(
11041145
self,
11051146
to_list: List[str],

0 commit comments

Comments
 (0)