How to Set Up Google OAuth for Gmail, Calendar, and Drive Integration in Craft-Agents OSS
Craft-Agents OSS provides a built-in PKCE-based OAuth 2.0 implementation that automatically handles authentication flows for Gmail, Calendar, Drive, and other Google APIs through declarative source configuration or programmatic API calls.
Craft-Agents OSS provides a built-in authentication system that eliminates the need for external OAuth libraries when integrating with Google APIs. The platform supports both declarative configuration through source definitions and programmatic access via the google-oauth.ts module. This implementation follows the OAuth 2.0 PKCE pattern to securely authenticate users and maintain long-lived access through refresh tokens.
Understanding the OAuth Flow Architecture
The Google OAuth implementation in packages/shared/src/auth/google-oauth.ts orchestrates the complete authorization flow as implemented in craft-ai-agents/craft-agents-oss from initial request to token refresh. The architecture follows the standard OAuth 2.0 PKCE pattern with specific optimizations for headless and interactive environments.
PKCE Implementation and Authorization URL Generation
The flow begins with credential resolution and PKCE generation. When a source configuration specifies googleOAuthClientId and googleOAuthClientSecret, these values take precedence; otherwise, the system falls back to the environment variables GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET as defined in lines 25-31 of google-oauth.ts.
The startGoogleOAuth function constructs the authorization URL targeting https://accounts.google.com/o/oauth2/v2/auth with the following critical parameters:
access_type=offlineto ensure a refresh token is issued- PKCE challenge derived from a randomly generated verifier
statetoken for CSRF protection- Scopes determined by either the
googleServicefield or customgoogleScopesarray
Token Exchange and User Validation
After the user authenticates through the browser, a local HTTP server created by createCallbackServer captures the authorization code. The exchangeCodeForTokens function then POSTs the code, PKCE verifier, and client credentials to https://oauth2.googleapis.com/token, receiving an access_token, optional refresh_token, and expires_in value.
Subsequently, the system validates the authentication by fetching the user's email from https://www.googleapis.com/oauth2/v2/userinfo, returning a complete GoogleOAuthResult containing the tokens, expiry timestamp, and user identity.
Configuring Google OAuth Credentials
Craft-Agents OSS supports flexible credential management through source configuration or environment variables, allowing secure deployment across development and production environments.
Environment Variables vs Source Configuration
You can define credentials at the system level using environment variables:
GOOGLE_OAUTH_CLIENT_IDGOOGLE_OAUTH_CLIENT_SECRET
Alternatively, provide credentials per-source in the JSON or YAML configuration using googleOAuthClientId and googleOAuthClientSecret fields. This approach enables multi-tenant scenarios where different sources use distinct Google Cloud projects.
Scope Selection Strategies
The platform provides two methods for defining OAuth scopes. The service-based approach uses the googleService field to automatically select predefined scopes for Gmail, Calendar, Drive, Docs, Sheets, YouTube, or Search Console. The custom scope approach allows explicit definition via the googleScopes array, which the getGoogleScopes helper merges with the mandatory userinfo.email scope (lines 76-84 of google-oauth.ts).
Source Configuration Examples
Configure Google OAuth through declarative source definitions that the source manager automatically processes when provider: "google" is specified.
Gmail Integration
The following configuration enables Gmail API access using the predefined service scope:
{
"slug": "my-gmail",
"provider": "google",
"api": {
"baseUrl": "https://gmail.googleapis.com",
"authType": "oauth",
"googleService": "gmail",
"googleOAuthClientId": "YOUR_CLIENT_ID",
"googleOAuthClientSecret": "YOUR_CLIENT_SECRET"
}
}
The googleService: "gmail" field automatically selects the appropriate Gmail scopes from the GOOGLE_SERVICE_SCOPES definition in google-oauth.ts (lines 40-52).
Calendar with Custom Scopes
For granular permissions, override the default scopes with specific access rights:
{
"slug": "my-calendar",
"provider": "google",
"api": {
"baseUrl": "https://calendar.googleapis.com",
"authType": "oauth",
"googleScopes": [
"https://www.googleapis.com/auth/calendar.events.readonly",
"https://www.googleapis.com/auth/userinfo.email"
],
"googleOAuthClientId": "YOUR_CLIENT_ID",
"googleOAuthClientSecret": "YOUR_CLIENT_SECRET"
}
}
Custom scopes bypass the predefined service sets while maintaining the automatic inclusion of userinfo.email through the getGoogleScopes function.
Drive Access Using Default Service Scopes
The simplest configuration for Google Drive uses the service preset:
{
"slug": "my-drive",
"provider": "google",
"api": {
"baseUrl": "https://drive.googleapis.com",
"authType": "oauth",
"googleService": "drive",
"googleOAuthClientId": "YOUR_CLIENT_ID",
"googleOAuthClientSecret": "YOUR_CLIENT_SECRET"
}
}
This configuration automatically requests https://www.googleapis.com/auth/drive plus the email verification scope.
Implementing OAuth Programmatically
For custom tools or dynamic authentication flows, import the OAuth functions directly from @craft-agents/shared.
Using startGoogleOAuth
Invoke the authorization flow programmatically when you need to authenticate outside the standard source configuration:
import { startGoogleOAuth } from '@craft-agents/shared/auth/google-oauth';
async function authenticateDrive() {
const result = await startGoogleOAuth({
service: 'drive',
clientId: process.env.GOOGLE_OAUTH_CLIENT_ID,
clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET,
});
if (!result.success) {
console.error('OAuth failed:', result.error);
return;
}
console.log('Access token:', result.accessToken);
console.log('Refresh token:', result.refreshToken);
console.log('User email:', result.email);
}
The GoogleOAuthResult object includes the client credentials, enabling you to store the complete authentication context for later use.
Token Refresh Handling
Long-lived integrations require token refresh when the access token expires. The refreshGoogleToken function (lines 89-99 of google-oauth.ts) reuses the stored client ID, client secret, and refresh token to obtain new credentials without user interaction:
import { refreshGoogleToken } from '@craft-agents/shared/auth/google-oauth';
const newTokens = await refreshGoogleToken({
clientId: storedCredentials.clientId,
clientSecret: storedCredentials.clientSecret,
refreshToken: storedCredentials.refreshToken,
});
This function handles the POST request to https://oauth2.googleapis.com/token with grant_type=refresh_token and returns updated token data.
Key Implementation Files
The Google OAuth system spans several modules in the packages/shared directory:
-
packages/shared/src/auth/google-oauth.ts– Core OAuth flow implementation including PKCE generation, token exchange, and refresh logic. -
packages/shared/src/sources/types.ts– Defines theGoogleServicetype and source configuration interfaces (lines 64-70). -
packages/shared/src/config/validators.ts– Zod schema validation for Google-specific configuration fields. -
packages/shared/src/sources/credential-manager.ts– Extracts OAuth credentials from source configurations and builds authentication contexts. -
packages/shared/src/sources/server-builder.ts– Determines OAuth requirements per source and constructs appropriate HTTP clients with automatic token refresh.
Summary
-
PKCE-based security: The implementation uses Proof Key for Code Exchange to secure the OAuth flow without requiring a client secret in the browser.
-
Flexible credential management: Support for both environment variables (
GOOGLE_OAUTH_CLIENT_ID,GOOGLE_OAUTH_CLIENT_SECRET) and per-source configuration fields. -
Automatic scope resolution: The
googleServicefield selects predefined scopes for Gmail, Calendar, Drive, and other services, whilegoogleScopesallows custom permission definitions. -
Built-in token refresh: The
refreshGoogleTokenfunction automatically handles access token renewal using stored refresh tokens. -
Source manager integration: Declarative configuration with
provider: "google"automatically triggers the OAuth flow and maintains authentication state.
Frequently Asked Questions
What is the difference between using googleService and googleScopes?
The googleService field selects predefined scope sets optimized for specific Google APIs like Gmail or Drive, automatically including necessary permissions plus userinfo.email. The googleScopes field allows complete customization of OAuth permissions, overriding any predefined service scopes while maintaining the email verification requirement. Both approaches are validated through getGoogleScopes in packages/shared/src/auth/google-oauth.ts.
How does token refresh work in Craft-Agents OSS?
When an access token expires, the system calls refreshGoogleToken (lines 89-99 of google-oauth.ts) with the stored client credentials and refresh token. This function exchanges the refresh token for a new access token at https://oauth2.googleapis.com/token without requiring user interaction. The source manager in server-builder.ts automatically handles this refresh when constructing API clients.
Can I use environment variables instead of hardcoding credentials?
Yes. If you omit googleOAuthClientId and googleOAuthClientSecret from the source configuration, the credential manager automatically falls back to the GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET environment variables. This approach is recommended for production deployments to keep sensitive credentials out of configuration files.
What happens if the OAuth callback fails or is interrupted?
The startGoogleOAuth function launches a local HTTP server via createCallbackServer to capture the authorization code. If the callback fails or the user closes the browser, the function returns a GoogleOAuthResult with success: false and an error message describing the failure. Your application should handle this case by logging the error and optionally prompting the user to retry the authentication flow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →