Merge pull request #335 from CREDO23/connector-airtable-refresh-token

[Feature] Airtable connector | Refresh access token when expired
This commit is contained in:
Rohan Verma 2025-09-28 20:58:38 -07:00 committed by GitHub
commit 3fec106c31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 80 additions and 1 deletions

View file

@ -280,3 +280,78 @@ async def airtable_callback(
raise HTTPException( raise HTTPException(
status_code=500, detail=f"Failed to complete Airtable OAuth: {e!s}" status_code=500, detail=f"Failed to complete Airtable OAuth: {e!s}"
) from e ) from e
async def refresh_airtable_token(
session: AsyncSession, connector: SearchSourceConnector
):
"""
Refresh the Airtable access token for a connector.
Args:
session: Database session
connector: Airtable connector to refresh
Returns:
Updated connector object
"""
try:
logger.info(f"Refreshing Airtable token for connector {connector.id}")
credentials = AirtableAuthCredentialsBase.from_dict(connector.config)
auth_header = make_basic_auth_header(
config.AIRTABLE_CLIENT_ID, config.AIRTABLE_CLIENT_SECRET
)
# Prepare token refresh data
refresh_data = {
"grant_type": "refresh_token",
"refresh_token": credentials.refresh_token,
"client_id": config.AIRTABLE_CLIENT_ID,
"client_secret": config.AIRTABLE_CLIENT_SECRET,
}
async with httpx.AsyncClient() as client:
token_response = await client.post(
TOKEN_URL,
data=refresh_data,
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": auth_header,
},
timeout=30.0,
)
if token_response.status_code != 200:
raise HTTPException(
status_code=400, detail="Token refresh failed: {token_response.text}"
)
token_json = token_response.json()
# Calculate expiration time (UTC, tz-aware)
expires_at = None
if token_json.get("expires_in"):
now_utc = datetime.now(UTC)
expires_at = now_utc + timedelta(seconds=int(token_json["expires_in"]))
# Update credentials object
credentials.access_token = token_json["access_token"]
credentials.expires_in = token_json.get("expires_in")
credentials.expires_at = expires_at
credentials.scope = token_json.get("scope")
# Update connector config
connector.config = credentials.to_dict()
await session.commit()
await session.refresh(connector)
logger.info(
f"Successfully refreshed Airtable token for connector {connector.id}"
)
return connector
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Failed to refresh Airtable token: {e!s}"
) from e

View file

@ -8,6 +8,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.config import config from app.config import config
from app.connectors.airtable_connector import AirtableConnector from app.connectors.airtable_connector import AirtableConnector
from app.db import Document, DocumentType, SearchSourceConnectorType from app.db import Document, DocumentType, SearchSourceConnectorType
from app.routes.airtable_add_connector_route import refresh_airtable_token
from app.schemas.airtable_auth_credentials import AirtableAuthCredentialsBase from app.schemas.airtable_auth_credentials import AirtableAuthCredentialsBase
from app.services.llm_service import get_user_long_context_llm from app.services.llm_service import get_user_long_context_llm
from app.services.task_logging_service import TaskLoggingService from app.services.task_logging_service import TaskLoggingService
@ -102,7 +103,10 @@ async def index_airtable_records(
"Credentials expired", "Credentials expired",
{"error_type": "ExpiredCredentials"}, {"error_type": "ExpiredCredentials"},
) )
return 0, "Airtable credentials have expired. Please re-authenticate."
connector = await refresh_airtable_token(session, connector)
# return 0, "Airtable credentials have expired. Please re-authenticate."
# Calculate date range for indexing # Calculate date range for indexing
start_date_str, end_date_str = calculate_date_range( start_date_str, end_date_str = calculate_date_range(