Connect your app

Connect your app

Point your app's OAuth and Google Calendar SDK clients at Calendarpit while keeping production Google settings switchable.

Add a provider switch

Your app must choose Calendarpit explicitly. Starting Calendarpit does not intercept network traffic to Google.

To use Calendarpit in local development or automated tests, add a provider switch. Put it in the code that creates your OAuth client and Google Calendar SDK client.

Environment variable names such as CALENDARPIT_URL are conventions in your app. Google SDKs do not read CALENDARPIT_URL automatically. Your initialization code must read your configuration and pass the local URLs to the SDK.

Keep your provider credentials separate:

  • Use Calendarpit access tokens and refresh tokens only with the local provider.
  • Keep real Google client secrets and tokens in your Google configuration.

Never put real Google credentials in a fixture.

Override every provider endpoint

Override all five endpoints together. Changing only the Calendar API URL is not enough.

If OAuth stays on its default endpoints, your app still sends these requests to Google:

  • Browser redirects
  • Token exchanges
  • Token refreshes
PurposeLocal endpoint
Authorization screenhttp://127.0.0.1:4173/oauth/authorize
Code exchange and refreshhttp://127.0.0.1:4173/oauth/token
User profilehttp://127.0.0.1:4173/userinfo
Calendar API roothttp://127.0.0.1:4173/
Token revocationhttp://127.0.0.1:4173/oauth/revoke

Calendar API URL format by SDK

Each SDK expects a different form of the Calendar API URL:

  • JavaScript (@googleapis/calendar): Appends /calendar/v3/... to rootUrl. Do not include /calendar/v3 twice.
  • Python, Go, and .NET: Expect the full service URL, including /calendar/v3/.
  • Java: Sets the root URL and service path separately.

See SDK examples.

Watch callback address

A watch callback address points in the opposite direction: from Calendarpit to your app. An example is http://127.0.0.1:4175/notifications.

By default, Calendarpit allows only loopback callback hosts (127.0.0.1, localhost, [::1]). To allow extra hosts, list them in CALENDARPIT_WATCH_HOSTS. See Watch notifications.

Register a test OAuth client

Register your app in the clients array of your fixture. Calendarpit checks these values:

  • Authorization endpoint: client_id and redirect_uri.
  • /oauth/token: client_id and client_secret, plus redirect_uri for code exchange.
{
  "clients": [
    {
      "id": "my-app",
      "secret": "local-test-secret",
      "redirectUris": ["http://127.0.0.1:3000/auth/google/callback"]
    }
  ]
}

Two fields need care:

  • redirectUris must contain your app's exact callback URL. Calendarpit matches redirect_uri by exact string equality. It does not apply port wildcards or trailing-slash normalization.
  • secret is required. Your app must send it as client_secret (or HTTP Basic) on every /oauth/token request, including PKCE flows.

How the fixture is applied

A fixture replaces the whole workspace. Include users and calendars alongside clients. If you use the sample app, keep the sample-app entry.

When the fixture takes effect depends on how you pass it:

  • Startup fixture: Applies only to a new database or a :memory: session.
  • Running or persisted workspace: Load the fixture with POST /__calendarpit/fixtures, or start with a new database.

A reset restores the startup fixture, not a fixture loaded through the API. To keep your client across resets, do one of the following:

  • Pass the fixture at startup (--config or CALENDARPIT_CONFIG) with a new database or :memory: session.
  • Reload the fixture after each reset.

See Fixtures.

Built-in client

The built-in default fixture registers one OAuth client for local testing:

  • Client ID: sample-app
  • Client secret: local-sample-secret (a public local test value)
  • Redirect URI: http://127.0.0.1:4175/callback

For the full authorization code, refresh, /userinfo, and revocation flow, see Local OAuth.

JavaScript OAuth and Calendar client

With @googleapis/calendar and google-auth-library, configure two settings:

  • endpoints on OAuth2Client
  • rootUrl on the Calendar client
import { calendar } from '@googleapis/calendar';
import { OAuth2Client } from 'google-auth-library';

const origin = 'http://127.0.0.1:4173';
const auth = new OAuth2Client({
  clientId: 'sample-app',
  clientSecret: 'local-sample-secret',
  redirectUri: 'http://127.0.0.1:4175/callback',
  endpoints: {
    oauth2AuthBaseUrl: `${origin}/oauth/authorize`,
    oauth2TokenUrl: `${origin}/oauth/token`,
    oauth2RevokeUrl: `${origin}/oauth/revoke`,
  },
});
const api = calendar({ version: 'v3', auth, rootUrl: origin });
// After the callback: auth.setCredentials((await auth.getToken(code)).tokens)
const profile = await auth.request({ url: `${origin}/userinfo` });
const calendars = await api.calendarList.list();
const { data } = await api.freebusy.query({
  requestBody: {
    timeMin: '2026-10-19T00:00:00+09:00',
    timeMax: '2026-10-20T00:00:00+09:00',
    items: [{ id: 'primary' }],
  },
});
console.log(data.calendars?.primary?.busy);

Scopes

Authorize the grant with scopes that cover all three calls in this example. For example: auth.generateAuthUrl({ access_type: 'offline', scope: ['https://www.googleapis.com/auth/calendar', 'email', 'profile'] }).

  • calendarList.list requires calendar, calendar.readonly, calendar.calendarlist, or calendar.calendarlist.readonly. Narrower scopes such as calendar.events or calendar.freebusy do not cover it.
  • /userinfo requires openid, email, profile, or a userinfo.* scope. It returns email only with an email scope, and name only with a profile scope.
  • Refresh tokens are issued only with access_type=offline.

See Local OAuth for the full scope list.

Token refresh and unsupported helpers

After you call auth.setCredentials, token refresh requests from OAuth2Client also use the local oauth2TokenUrl.

Other OAuth2Client helpers, such as getTokenInfo and verifyIdToken, still call Google. This changes only if you also override tokenInfoUrl and the certificate URLs, as the sample app does.

Calendarpit does not support these endpoints or issue ID tokens. Do not call those helpers against the local provider.

For Python, Go, and other languages, see SDK examples.

Reference sample app

The repository includes an optional sample app using @googleapis/calendar and google-auth-library. It is not bundled with Calendarpit. You do not need it to connect your own app.

To try it, prepare a source checkout using the development guide. Then run these commands in separate terminals:

# Terminal 1: start the packaged server
calendarpit serve

# Terminal 2: from the prepared source checkout
bun run sample:dev

Calendarpit runs at http://127.0.0.1:4173, and the sample app runs at http://127.0.0.1:4175.

Patterns to reuse

The sample app is a single-connection local demo. It shows two patterns for your own integration:

  • State cookie verification: It stores the OAuth state parameter in a 5-minute HttpOnly cookie (sample_state). It verifies the value on /callback before exchanging the authorization code.
  • Loopback-only outbound guard: It blocks outbound SDK requests to hosts other than 127.0.0.1, localhost, or [::1]. It also rejects HTTP redirects. Together, these prevent accidental requests to real Google servers.

Environment variables

The sample app uses these environment variables:

Environment variableDefault
CALENDARPIT_URLhttp://127.0.0.1:4173
SAMPLE_PORT4175
SAMPLE_CLIENT_IDsample-app
SAMPLE_CLIENT_SECRETlocal-sample-secret
AUTHORIZATION_URL${CALENDARPIT_URL}/oauth/authorize
TOKEN_URL${CALENDARPIT_URL}/oauth/token
USERINFO_URL${CALENDARPIT_URL}/userinfo
CALENDAR_API_URL${CALENDARPIT_URL}

The sample app also sets these values:

  • Revocation URL: ${CALENDARPIT_URL}/oauth/revoke
  • Redirect URI: http://127.0.0.1:<SAMPLE_PORT>/callback

The built-in sample-app client registers only port 4175. If you change SAMPLE_PORT, update the client's redirectUris in a fixture.

Checklist for your app

When you maintain a Calendarpit integration in your app, verify:

  • All endpoint overrides: Authorization, token exchange, token refresh, user profile, Calendar API calls, and token revocation all use the local endpoints.
  • Callback reachability:
    • The browser can reach your app's OAuth redirect_uri.
    • Calendarpit can reach your app's watch webhook address. If the webhook host is not loopback, set CALENDARPIT_WATCH_HOSTS.
  • Full application flow: Test your UI and background workers across login, event reads and writes, watch notifications, token refresh, revocation, and workspace reset.
  • App-specific fixtures: Keep your app's fixtures and E2E test scenarios in its own repository.

On this page