Skip to content

chore: fix oauth_calendar_agent example #1423

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 16, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 79 additions & 41 deletions contributing/samples/oauth_calendar_agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
from google.adk.auth import AuthCredentialTypes
from google.adk.auth import OAuth2Auth
from google.adk.tools import ToolContext
from google.adk.tools.authenticated_tool.base_authenticated_tool import AuthenticatedFunctionTool
from google.adk.tools.authenticated_tool.credentials_store import ToolContextCredentialsStore
from google.adk.tools.google_api_tool import CalendarToolset
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
Expand Down Expand Up @@ -58,7 +56,6 @@ def list_calendar_events(
end_time: str,
limit: int,
tool_context: ToolContext,
credential: AuthCredential,
) -> list[dict]:
"""Search for calendar events.

Expand All @@ -83,11 +80,84 @@ def list_calendar_events(
Returns:
list[dict]: A list of events that match the search criteria.
"""

creds = Credentials(
token=credential.oauth2.access_token,
refresh_token=credential.oauth2.refresh_token,
)
creds = None

# Check if the tokes were already in the session state, which means the user
# has already gone through the OAuth flow and successfully authenticated and
# authorized the tool to access their calendar.
if "calendar_tool_tokens" in tool_context.state:
creds = Credentials.from_authorized_user_info(
tool_context.state["calendar_tool_tokens"], SCOPES
)
if not creds or not creds.valid:
# If the access token is expired, refresh it with the refresh token.
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
auth_scheme = OAuth2(
flows=OAuthFlows(
authorizationCode=OAuthFlowAuthorizationCode(
authorizationUrl="https://accounts.google.com/o/oauth2/auth",
tokenUrl="https://oauth2.googleapis.com/token",
scopes={
"https://www.googleapis.com/auth/calendar": (
"See, edit, share, and permanently delete all the"
" calendars you can access using Google Calendar"
)
},
)
)
)
auth_credential = AuthCredential(
auth_type=AuthCredentialTypes.OAUTH2,
oauth2=OAuth2Auth(
client_id=oauth_client_id, client_secret=oauth_client_secret
),
)
# If the user has not gone through the OAuth flow before, or the refresh
# token also expired, we need to ask users to go through the OAuth flow.
# First we check whether the user has just gone through the OAuth flow and
# Oauth response is just passed back.
auth_response = tool_context.get_auth_response(
AuthConfig(
auth_scheme=auth_scheme, raw_auth_credential=auth_credential
)
)
if auth_response:
# ADK exchanged the access token already for us
access_token = auth_response.oauth2.access_token
refresh_token = auth_response.oauth2.refresh_token

creds = Credentials(
token=access_token,
refresh_token=refresh_token,
token_uri=auth_scheme.flows.authorizationCode.tokenUrl,
client_id=oauth_client_id,
client_secret=oauth_client_secret,
scopes=list(auth_scheme.flows.authorizationCode.scopes.keys()),
)
else:
# If there are no auth response which means the user has not gone
# through the OAuth flow yet, we need to ask users to go through the
# OAuth flow.
tool_context.request_credential(
AuthConfig(
auth_scheme=auth_scheme,
raw_auth_credential=auth_credential,
)
)
# The return value is optional and could be any dict object. It will be
# wrapped in a dict with key as 'result' and value as the return value
# if the object returned is not a dict. This response will be passed
# to LLM to generate a user friendly message. e.g. LLM will tell user:
# "I need your authorization to access your calendar. Please authorize
# me so I can check your meetings for today."
return "Need User Authorization to access their calendar."
# We store the access token and refresh token in the session state for the
# next runs. This is just an example. On production, a tool should store
# those credentials in some secure store or properly encrypt it before store
# it in the session state.
tool_context.state["calendar_tool_tokens"] = json.loads(creds.to_json())

service = build("calendar", "v3", credentials=creds)
events_result = (
Expand Down Expand Up @@ -138,38 +208,6 @@ def update_time(callback_context: CallbackContext):

Currnet time: {_time}
""",
tools=[
AuthenticatedFunctionTool(
func=list_calendar_events,
auth_config=AuthConfig(
auth_scheme=OAuth2(
flows=OAuthFlows(
authorizationCode=OAuthFlowAuthorizationCode(
authorizationUrl=(
"https://accounts.google.com/o/oauth2/auth"
),
tokenUrl="https://oauth2.googleapis.com/token",
scopes={
"https://www.googleapis.com/auth/calendar": (
"See, edit, share, and permanently delete"
" all the calendars you can access using"
" Google Calendar"
)
},
)
)
),
raw_auth_credential=AuthCredential(
auth_type=AuthCredentialTypes.OAUTH2,
oauth2=OAuth2Auth(
client_id=oauth_client_id,
client_secret=oauth_client_secret,
),
),
),
credential_store=ToolContextCredentialsStore(),
),
calendar_toolset,
],
tools=[list_calendar_events, calendar_toolset],
before_agent_callback=update_time,
)