How Craft Agents OSS Manages OAuth Flows for Google, Slack, and Microsoft Integrations

Craft Agents OSS implements a unified, type-safe OAuth 2.0 architecture that uses PKCE for Google and Microsoft, a Cloudflare relay for Slack's HTTPS requirements, and a shared callback server to handle token exchange and automatic credential persistence.

Craft Agents OSS provides a consistent developer experience for authenticating with third-party providers through a modular OAuth 2.0 implementation. The codebase in craft-ai-agents/craft-agents-oss abstracts provider-specific differences—such as PKCE requirements and redirect URI constraints—into reusable components while maintaining type safety across Google, Slack, and Microsoft integrations.

Unified OAuth Architecture

The OAuth flow management in Craft Agents OSS follows a standardized three-phase pattern regardless of provider. Each integration implements preparation, callback handling, and token exchange through provider-specific modules that share common infrastructure.

The core components include:

  • OAuth preparation – Functions like prepareGoogleOAuth, prepareSlackOAuth, and prepareMicrosoftOAuth build authorization URLs, generate PKCE verifier/challenge pairs (where required), and create CSRF-state tokens.
  • Callback server – The createCallbackServer function in packages/shared/src/auth/callback-server.ts creates a temporary local HTTP server that receives provider redirects and resolves a promise with the authorization code.
  • Token exchange – Provider-specific exchangeCodeForTokens functions POST to respective token endpoints and normalize responses into {accessToken, refreshToken, expiresAt}.
  • Credential management – The SourceCredentialManager in packages/shared/src/sources/credential-manager.ts persists tokens and handles automatic refresh.

Google OAuth Flow Implementation

The Google integration utilizes a PKCE-enabled flow suitable for public desktop clients. Located in packages/shared/src/auth/google-oauth.ts, this implementation supports both secret-less operation and traditional client-secret authentication when configured.

Key characteristics include:

  • PKCE generation – The flow generates a code_challenge and code_verifier using generatePKCE to prevent authorization code interception.
  • Predefined scopes – Service-specific scope sets are stored in GOOGLE_SERVICE_SCOPES, covering Gmail, Calendar, Drive, and other Google services.
  • Token endpoint – Exchanges codes at https://oauth2.googleapis.com/token.
  • User identification – Fetches the user's email from https://www.googleapis.com/oauth2/v2/userinfo.

To initiate authentication, call startGoogleOAuth with either a service identifier or custom scopes:

import { startGoogleOAuth, GoogleOAuthResult } from '@/shared/auth/google-oauth';

// Authenticate for Gmail using predefined scopes
async function authGoogleGmail(): Promise<GoogleOAuthResult> {
  return await startGoogleOAuth({ service: 'gmail' });
}

// Authenticate with custom spreadsheet permissions
async function authGoogleSheets(): Promise<GoogleOAuthResult> {
  return await startGoogleOAuth({
    scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly'],
  });
}

The returned GoogleOAuthResult contains accessToken, optional refreshToken, expiresAt, the user's email, and client credentials for subsequent refresh operations.

Slack OAuth Flow Implementation

Slack's integration diverges from the PKCE pattern and instead uses a user-token flow with a Cloudflare relay to handle HTTPS requirements. The implementation resides in packages/shared/src/auth/slack-oauth.ts, with relay utilities in packages/shared/src/auth/oauth-relay.ts.

Distinctive aspects include:

  • User-scope parameter – Uses user_scope rather than scope to obtain tokens that act on behalf of the authenticated user rather than a bot.
  • No PKCE – Slack does not implement PKCE for this flow.
  • HTTPS redirect handling – Because Slack requires HTTPS redirect URIs, the flow uses wrapPreparedOAuthFlowForRelay to redirect through https://agents.craft.do/auth/slack/callback, which forwards to the local HTTP callback server.
  • State envelope – The encodeOAuthRelayState function wraps the OAuth state in a signed envelope containing a returnTo URL to prevent CSRF attacks.

Authentication is initiated through startSlackOAuth:

import { startSlackOAuth, SlackOAuthResult } from '@/shared/auth/slack-oauth';

// Full workspace access
async function authSlackFull(): Promise<SlackOAuthResult> {
  return await startSlackOAuth({ service: 'full' });
}

// Limited to messaging permissions only
async function authSlackMessaging(): Promise<SlackOAuthResult> {
  return await startSlackOAuth({ service: 'messaging' });
}

The SlackOAuthResult includes accessToken, optional refreshToken (when token rotation is enabled), teamId, teamName, and userId.

Microsoft OAuth Flow Implementation

Microsoft's integration follows a PKCE-enabled pattern similar to Google but targets the Microsoft Graph API. The source code in packages/shared/src/auth/microsoft-oauth.ts handles both personal and organizational accounts through the common tenant endpoint.

Implementation details:

  • PKCE support – Uses PKCE for public clients, eliminating the need for client secrets in desktop environments.
  • Common tenant endpoint – Authenticates both personal Microsoft accounts and Azure AD work accounts via https://login.microsoftonline.com/common/oauth2/v2.0/token.
  • Required scopes – Always includes User.Read and offline_access alongside service-specific scopes defined in MICROSOFT_SERVICE_SCOPES.
  • User profile – Retrieves email via https://graph.microsoft.com/v1.0/me.

Invoke the flow using startMicrosoftOAuth:

import { startMicrosoftOAuth, MicrosoftOAuthResult } from '@/shared/auth/microsoft-oauth';

// Outlook mail access
async function authMicrosoftOutlook(): Promise<MicrosoftOAuthResult> {
  return await startMicrosoftOAuth({ service: 'outlook' });
}

// Custom Microsoft Graph scopes
async function authMicrosoftTasks(): Promise<MicrosoftOAuthResult> {
  return await startMicrosoftOAuth({
    scopes: ['https://graph.microsoft.com/Tasks.ReadWrite'],
  });
}

The resulting MicrosoftOAuthResult contains accessToken, refreshToken, expiresAt, and the authenticated user's email.

Shared Infrastructure Components

Three provider-agnostic utilities support all OAuth flows in Craft Agents OSS:

Callback Server – Implemented in packages/shared/src/auth/callback-server.ts, the createCallbackServer function launches a temporary HTTP listener on a local port. It renders a success or failure HTML page to the user and resolves a promise with the query parameters (code and state) received from the provider's redirect.

OAuth Relay – Defined in packages/shared/src/auth/oauth-relay.ts, this component handles Slack's HTTPS requirement. The encodeOAuthRelayState function creates a signed envelope for the state parameter, while wrapPreparedOAuthFlowForRelay rewrites the redirect URI to the Cloudflare relay endpoint.

Credential Manager – The SourceCredentialManager class in packages/shared/src/sources/credential-manager.ts persists OAuth credentials securely and automatically refreshes expired tokens using provider-specific refresh functions: refreshGoogleToken, refreshSlackToken, and refreshMicrosoftToken.

End-to-End Flow Execution

When a user creates a source requiring OAuth, the application executes a consistent sequence across all providers:

  1. Configuration validation – Checks for required client IDs and secrets using isGoogleOAuthConfigured, isSlackOAuthConfigured, or isMicrosoftOAuthConfigured.
  2. Server initialization – Creates a callback server via createCallbackServer to obtain a local redirect_uri.
  3. URL construction – Builds the provider-specific authorization URL with PKCE parameters (Google/Microsoft) or relay state (Slack).
  4. Browser launch – Opens the user's default browser using the cross-platform openUrl utility.
  5. Callback handling – Awaits the promise resolution from the callback server containing the authorization code.
  6. Token exchange – Validates the CSRF state and exchanges the code for tokens via the provider's token endpoint.
  7. Persistence – Stores the resulting credentials via SourceCredentialManager for automatic signing of subsequent API requests.

This architecture keeps provider-specific logic encapsulated in the individual *oauth.ts modules while allowing the higher-level source creation logic to remain provider-agnostic.

Summary

  • Craft Agents OSS unifies OAuth 2.0 flows for Google, Slack, and Microsoft through a modular architecture in packages/shared/src/auth/.
  • Google and Microsoft use PKCE-enabled flows without requiring client secrets, while Slack uses a user-token flow with a Cloudflare relay to handle HTTPS redirects.
  • The createCallbackServer function provides a temporary local HTTP listener for all providers to capture authorization codes.
  • Provider-specific modules handle token exchange and refresh (exchangeCodeForTokens, refreshGoogleToken, etc.), while the SourceCredentialManager handles persistence and automatic refresh.
  • All flows return strongly-typed results (GoogleOAuthResult, SlackOAuthResult, MicrosoftOAuthResult) containing access tokens, refresh tokens, and user identifiers.

Frequently Asked Questions

How does Craft Agents OSS handle Slack's requirement for HTTPS redirect URIs?

Since Slack requires HTTPS redirect URIs but the application runs a local HTTP server, Craft Agents OSS uses a Cloudflare relay at https://agents.craft.do/auth/slack/callback. The wrapPreparedOAuthFlowForRelay function in packages/shared/src/auth/oauth-relay.ts encodes the local callback URL and CSRF state into a signed envelope. When Slack redirects to the relay, it forwards the request to the local callback server, allowing the flow to complete without local HTTPS setup.

What is the difference between the Google and Slack OAuth implementations in Craft Agents OSS?

The Google implementation in packages/shared/src/auth/google-oauth.ts uses PKCE (Proof Key for Code Exchange) with code_challenge and code_verifier parameters to secure the authorization code flow, making it suitable for public clients without client secrets. The Slack implementation in packages/shared/src/auth/slack-oauth.ts does not use PKCE; instead, it uses a user_scope parameter and requires the Cloudflare relay to handle HTTPS redirects. Additionally, Slack's state parameter is wrapped in a signed envelope to include return URL information.

How are OAuth tokens refreshed automatically in Craft Agents OSS?

The SourceCredentialManager in packages/shared/src/sources/credential-manager.ts stores refresh tokens alongside access tokens. When an access token expires, the manager calls provider-specific refresh functions—refreshGoogleToken, refreshSlackToken, or refreshMicrosoftToken—to exchange the refresh token for a new access token. This process is transparent to the user and ensures continuous API access without re-authentication.

Can custom OAuth scopes be requested for Microsoft and Google integrations?

Yes. While Craft Agents OSS provides predefined scopes for common services (accessed via the service parameter), both startGoogleOAuth and startMicrosoftOAuth accept a custom scopes array. For example, you can request specific Microsoft Graph permissions like Tasks.ReadWrite or Google Drive scopes by passing them directly to the initialization function, overriding the default service-specific scopes.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →