Module kit / Calendar and booking
Calendar Connection
calendar-booking/calendar-connection
| Kind | backend |
|---|---|
| Domain | calendar-booking |
| Maturity | prod |
| Reusable | extract |
| Stack | fastapi-0.118, sqlalchemy-2.0-sync, postgres, cryptography-46, google-auth-oauthlib-1.2, google-auth-2.41, google-api-python-client-2.154, icalendar-6.1, recurring-ical-events-3.5, caldav-1.4, requests-2.32, pydantic-2.12 |
| Depends on | _shared/fastapi-project-layout, _shared/auth-dependency-pattern, _shared/alembic-migrations, _shared/error-envelope |
| Frontends | calendar-booking/ui/calendar-settings-panel |
| Reference implementation |
|
| Measured on | alchemy_labs main 62ba4fc41009b454e1c033e5036c7e8577db2061 2026-09-12 |
| Version | 1 |
Purpose
An admin or a workspace member connects a bookable calendar (Google via OAuth, a read-only iCal subscription URL, or a read+write CalDAV calendar) or disconnects one, so the booking system this domain also covers has a source of busy periods and, for Google/CalDAV, somewhere to write a confirmed appointment. The invariant that makes it trustworthy: every credential that can reach a calendar (Google's OAuth token pair, an iCal feed's bearer/basic/query credential, a CalDAV app password) is written through one shared Fernet cipher before it touches the database (CalendarIntegrationService.cipher, calendar_integration_service.py 70, 137 to 143), and a saved iCal or CalDAV connection can always be checked, synchronously and statelessly, against the real feed or server before or after saving (/integrations/test-ical, /integrations/test-caldav).
When to use
Trigger phrases: "connect Google Calendar", "connect iCal feed", "connect CalDAV", "calendar OAuth", "disconnect calendar", "platform integration" vs "workspace integration", feed_auth_type, CALENDAR_ENCRYPTION_KEY, test-ical, test-caldav, provider_account_id, is_platform_level, "why does the calendar say disconnected but the feed URL is still there".
Not this recipe: availability computation (get_available_slots/get_availability_slots, calendar_integration_service.py 517 to 1044) and the booking/cancel flow (create_booking/cancel_booking, 1050 to 1320, 1566 to 1621) that CONSUME an already-connected integration, a neighbouring recipe on the same service file and router; meeting-type CRUD (BookingMeetingType, /admin/meeting-types, /workspace/meeting-types); blocked times (CalendarBlockedTime, /admin/blocked-times); booking message dispatch (_dispatch_booking_messages, services/messaging_service); the OAuth callback handler is IN this recipe (it is the second half of "connect"), but the HTML popup page it returns and the settings UI that drives all of this are the frontend module below, not this file.
Stack assumptions
At 62ba4fc: fastapi==0.118.2, synchronous handlers on sqlalchemy.orm.Session (SQLAlchemy==2.0.43), cryptography==46.0.0 (Fernet), google-auth==2.41.1 / google-auth-oauthlib==1.2.1 / google-api-python-client==2.154.0 for the OAuth flow and the Calendar/oauth2 API clients, requests==2.32.5 for the iCal feed fetch and connectivity test, icalendar==6.1.0 for VEVENT parsing (recurring-ical-events==3.5.2 is an optional RRULE-expansion import used by the availability path, out of this recipe's scope), caldav==1.4.0 (RFC 4791 client, lazy-imported so the rest of the app doesn't pay its import cost), pydantic==2.12.0 for every request body, alembic==1.16.5, Postgres 15 in prod.
Foundations assumed: S4 (fastapi-project-layout) for the app and mount_router, which the router is mounted through at /api/calendar with surfaces=LABS_SURFACES (app_fastapi.py 845); S5 (auth-dependency-pattern) for CurrentUser/AdminUser (api/dependencies.py 565, 568, both built on get_current_user/get_current_admin_user); S8 (alembic-migrations) for the one schema delta this recipe owns; S7 (error-envelope) - every HTTPException this router raises is plain (raise HTTPException(status_code=..., detail=...)), formatted into the standard envelope only by the app-level @app.exception_handler(HTTPException) (app_fastapi.py 724 to 725), the router does no formatting of its own. Deliberately NOT depended on: _shared/workspace-scoping. Tenancy here is a private, inlined raw-SQL helper (_has_workspace_access, calendar_router.py 959 to 979) rather than the shared middleware pattern, and its own docstring gives the reason: api.dependencies re-exports via import *, which drops underscore-prefixed helpers, so a shared helper of that shape could not be imported the normal way (962 to 963).
Environment, read two different ways: GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET (SecretStr) / GOOGLE_CALENDAR_REDIRECT_URI are settings fields (core/config.py 619 to 624); unset makes _validate_google_config only log a warning at service construction (calendar_integration_service.py 114 to 135), the actual failure is deferred to get_oauth_authorization_url raising ValueError when either is falsy (149 to 159), which the router turns into a 400. CALENDAR_ENCRYPTION_KEY is NOT a settings field, it is read directly with os.getenv inside _get_or_create_key (78 to 112); see Gotchas for its fallback order. ENVIRONMENT is a settings field defaulting to "development" (core/config.py 30) and gates whether a missing key auto-creates a local file or raises.
Data model
calendar_integrations, one row per connected calendar, OAuth and iCal and CalDAV credential columns living side by side on the same table (models/calendar_integration_model.py 82 to 155; the only migration in this recipe's scope, 20260427b_add_ical_caldav.py, is the delta that added the iCal/CalDAV columns on top of the frozen baseline_prod_schema_001.py snapshot that already carried the OAuth columns):
calendar_integrations (
id uuid PRIMARY KEY,
workspace_id uuid REFERENCES workspaces(id) ON DELETE CASCADE, -- NULL = platform-level (admin's calendar)
user_id uuid REFERENCES users(id) ON DELETE SET NULL, -- who last completed the Google OAuth flow
provider varchar(50) NOT NULL DEFAULT 'google', -- google | outlook | apple | ical_feed | caldav
provider_account_id varchar(255), -- Google: account email; iCal/CalDAV: the account_email param
provider_account_name varchar(255),
access_token_encrypted text, -- Fernet ciphertext; nullable (only OAuth uses it); "" (not NULL) after disconnect
refresh_token_encrypted text, -- Fernet ciphertext; NULL after disconnect
token_expires_at timestamptz,
scopes jsonb DEFAULT '[]',
default_messages jsonb, -- meeting-type message inheritance; out of this recipe's scope
ical_feed_url text,
feed_auth_type varchar(20) DEFAULT 'none', -- none | basic | bearer | query_token
feed_auth_value_encrypted text, -- Fernet ciphertext of the bearer token / basic-auth password / query token
feed_auth_username varchar(255),
feed_etag varchar(255), -- conditional-GET cache, not a credential
feed_last_modified varchar(255),
feed_last_fetched_at timestamptz,
feed_last_event_count integer,
caldav_url text,
caldav_username varchar(255),
caldav_password_encrypted text, -- Fernet ciphertext; an APP password, never the account's main password
calendar_id varchar(255) DEFAULT 'primary',
timezone varchar(100) DEFAULT 'Europe/Zurich',
is_active boolean DEFAULT true,
connected_at timestamptz DEFAULT now(), last_sync_at timestamptz, last_error text,
created_at timestamptz DEFAULT now(), updated_at timestamptz
);
-- No CHECK constraints on this table. No UNIQUE on (workspace_id, provider) since
-- ical_caldav_001 dropped it: a workspace can hold multiple iCal feeds or CalDAV
-- calendars (one per doctor), model comment 148-149.
What is NOT stored: the Google OAuth app's own client credential (GOOGLE_CLIENT_SECRET is a process-level env var, not per-row); a CalDAV account's real login password (the column name and the module docstring both insist on an app password, caldav_provider.py 11 to 12); the Fernet key itself (lives outside this table, see Deploy & ops). Disconnecting an integration does NOT null feed_auth_value_encrypted or caldav_password_encrypted - only the two OAuth columns are cleared (Gotchas).
API surface
All routes mounted at /api/calendar (calendar_router.py, surfaces=LABS_SURFACES, i.e. ordinary Labs session auth, not surface_exempt).
| Route | Auth | Tenancy | Codes | Notes |
|---|---|---|---|---|
POST /admin/connect (273 to 302) | AdminUser | platform (workspace_id=None) | 200 {authorization_url, state, redirect_url}; 400 ValueError (Google creds not configured); 500 | starts Google OAuth for the platform integration |
GET /callback (866 to 952) | none - trusts an optional ?user_id= query param, else falls back to the first is_admin user (886 to 898) | derived from the encrypted state param (202 to 203) | always 200 HTML (a popup-closer page); success/failure is signalled to the opener via postMessage, never via HTTP status | shared by both admin and workspace OAuth; see Security & tenancy |
DELETE /admin/disconnect (305 to 319) | AdminUser | platform | 200 {success:true, message}; 404 no platform integration | |
POST /workspace/connect (1056 to 1084) | CurrentUser | _assert_calendar_connect_access | 200 same shape as admin/connect; 400 missing workspace_id / ValueError | |
DELETE /integrations/{integration_id} (471 to 495) | CurrentUser | admin OR _has_workspace_access on the row's workspace_id | 200 {success:true}; 403; 404 | generic disconnect, any provider |
POST /integrations/test-ical (362 to 371) | CurrentUser | none | 200 {ok:true,...} or {ok:false, error} - never a 4xx for a bad feed | stateless; no _assert_calendar_connect_access call (Security & tenancy) |
POST /integrations/test-caldav (374 to 378) | CurrentUser | none | same shape | stateless; same gap |
POST /integrations/ical (381 to 421) | CurrentUser | _assert_calendar_connect_access | 200 IntegrationResponse; 400 invalid auth_type (394 to 395) | encrypts auth_value only if provided |
POST /integrations/caldav (424 to 452) | CurrentUser | _assert_calendar_connect_access | 200 IntegrationResponse | always encrypts password, no optional branch |
GET /integrations (455 to 468) | AdminUser | none beyond admin | 200 list, filterable by workspace_id/provider query params | cross-tenant admin directory |
GET /admin/integration (249 to 270) | AdminUser | platform | 200 IntegrationResponse or null | |
GET /workspace/integration (1017 to 1027) | CurrentUser | admin OR _has_workspace_access | 200 or null; 403 | Google-only lookup, kept for legacy callers |
GET /workspace/integrations (1030 to 1054, not fully cited above) | CurrentUser | same | 200 list of active integrations, any provider | plural; superset of the Google-only singular route |
IntegrationResponse (45 to 59) never serializes a credential or token field: only id, workspace_id, provider, provider_account_id/name, calendar_id, timezone, is_active, connected_at, last_sync_at, is_platform_level.
Application logic
- Google OAuth start (
get_oauth_authorization_url, 149 to 188): builds aFlowfrom a literalclient_configdict assembled fromgoogle_client_id/google_client_secret/redirect_uri; encrypts{"workspace_id": ...}as the OAuthstateparam with the SAME Fernet cipher used for tokens (177 to 179); requestsaccess_type=offline, prompt=consentso a refresh token is reissued even on a re-consent (182 to 186). - Google OAuth callback (
handle_oauth_callback, 190 to 280): decryptsstateto recoverworkspace_id(202 to 203); exchanges the code viaflow.fetch_token; callsoauth2.userinfo().get()for the account email/name (226 to 229); looks up an existing row by(workspace_id, provider='google')only, not by which user is completing the flow (232 to 235) - a second person re-running OAuth for the same scope overwrites the first row's tokens rather than creating a second (see Gotchas); encrypts the access token unconditionally, the refresh token only if Google returned one, otherwise keeps the existing encrypted refresh token (239 to 240, 267); on a brand-new PLATFORM-level integration only (workspace_id is None), seeds the three fixed meeting types (254 to 255, 277 to 278,_create_default_meeting_types, 286 to 367) - a workspace-level connect never seeds anything. - iCal connect (
connect_ical_feed, 381 to 421):_assert_calendar_connect_accessfirst (393); validatesauth_typeagainst theFeedAuthTypeenum, 400 if not a member (394 to 395); encryptsauth_valueonly when one is given (397 to 400 - anone-auth feed stores no ciphertext column at all); inserts the row directly. Nothing server-side forces/integrations/test-icalto have been called first; the connectivity check is a frontend-driven convention, not a backend precondition. - CalDAV connect (
connect_caldav, 424 to 452): same access check (431); ALWAYS encryptspassword(432, no optional branch - even an empty-string password is stored as a Fernet ciphertext of the empty string); same "no forced test before save" note as step 3. - Connectivity tests (
test_ical_feed/test_caldav_connection, 362 to 378, delegating toical_provider.test_feed/caldav_provider.test_connection): stateless, write nothing to the database, meant to run from the UI before step 3/4, not enforced after the fact.test_feed(ical_provider.py265 to 307): builds the same auth header/query the real fetch path would use,GETs the URL with a 12s timeout, requires the literal substring"BEGIN:VCALENDAR"in the response body, returns{ok, status, bytes, event_count, etag, last_modified}; a network error, non-2xx, or missingVCALENDARmarker all become{ok:false, error/status}- the function never raises.test_connection(caldav_provider.py305 to 329): opens acaldav.DAVClient, tries the given URL as a calendar directly, falls back to principal discovery on failure, returns{ok:true, calendar_url, name}or, for a bare discovery URL,{ok:true, ..., available:[...]}listing every calendar found on the account; any exception becomes{ok:false, error}.
- Disconnect (
disconnect_integration, 459 to 472, called by both/admin/disconnectand the generic/integrations/{id}): setsis_active=False,access_token_encrypted="",refresh_token_encrypted=None, commits. Provider-agnostic - run on an iCal or CalDAV row exactly the same way, even though neither of those two columns holds that row's actual credential (Gotchas).
Invariants
- Every credential column that can reach a live calendar is written through
self.cipher, never plaintext:access_token_encrypted/refresh_token_encrypted(239 to 240, 266 to 267),feed_auth_value_encrypted(397 to 399),caldav_password_encrypted(432) - confirmed bytest_encrypt_decrypt_round_trip,test_connect_ical_encrypts_auth_value,test_connect_caldav_encrypts_password. - Fernet encryption is non-deterministic: two encryptions of the same credential never produce identical ciphertext, so ciphertext can never be used as an equality lookup key (
test_encrypt_token_nondeterministic). - A platform-level integration (
workspace_id IS NULL) is reachable, for every mutating route this recipe covers, only by an admin (_assert_calendar_connect_access989 to 993,AdminUseron every/admin/*route); a workspace-scoped integration is reachable by any ACTIVE member of that workspace, not only its owner (_has_workspace_access959 to 979;test_has_workspace_access_true_for_member,test_has_workspace_access_false_for_inactive_membership). get_platform_integrationandget_workspace_integrationonly ever return an active GOOGLE row (413 to 419, 421 to 436); a platform-level or workspace-level iCal/CalDAV row exists in the table but is invisible to both (test_service_get_platform_integration_ignores_non_google).- Disconnecting never deletes a row;
is_active=Falseis the only durable "gone" signal, and every lookup helper this recipe covers filters on it (test_primary_integration_skips_inactive,test_service_get_platform_integration_ignores_inactive).
Frontend
The module that would render this, by id:
calendar-booking/ui/calendar-settings-panel the connect/disconnect/test UI for all three
providers, at admin (platform) and workspace
scope; not written yet in this kit
(frontend_modules is required non-empty per
AUTHORING.md, this is the placeholder id)
Alchemy Labs' own (pre-Prima) frontend already implements exactly this surface, cited here only as what a rebuild would replace, not as this recipe's subject: alchemy_labs/frontend/src/pages/AdminCalendarSettings.tsx (969 lines) calls POST /api/calendar/admin/connect and DELETE /api/calendar/admin/disconnect; alchemy_labs/frontend/src/pages/CalendarSettings.tsx (1369 lines) calls POST /api/calendar/workspace/connect; alchemy_labs/frontend/src/components/calendar/AlternativeCalendarConnect.tsx (414 lines) calls all four of test-ical, integrations/ical, test-caldav, integrations/caldav in that connect-then-test-then-save order.
Minimum admin/debug surface with no dedicated route: GET /integrations?workspace_id=&provider= (admin-only, 455 to 468) is the only cross-tenant directory; there is no route that surfaces a decrypted credential or even confirms one exists beyond is_active/provider_account_id/provider_account_name - by design, IntegrationResponse never carries a credential field.
Security & tenancy
Auth dependency: CurrentUser/AdminUser (api/dependencies.py 565, 568), JWT bearer or httpOnly cookie or X-API-Key, resolved by get_current_user (187 to ~230) then, for admin routes, get_current_admin_user requiring is_admin (364 to 375). Workspace derivation is NOT the shared _shared/workspace-scoping middleware pattern; this router inlines its own raw-SQL primitive, _has_workspace_access (959 to 979, owner-or-active-member), with the reason given in its own docstring: api.dependencies re-exports via import *, which drops underscore-prefixed helpers (962 to 963). _assert_calendar_connect_access (982 to 998) is the connect-time gate actually used by /integrations/ical, /integrations/caldav, and /workspace/connect: an admin bypasses everything; a non-admin caller with no workspace_id is refused outright (990 to 993, the reason a "platform-only" 403 and a "no workspace supplied" 403 read identically in the tests); a non-admin caller naming a workspace_id they are not an active member of is refused (994 to 998).
Fail direction: closed for every route that calls _assert_calendar_connect_access - nothing is written before that check passes. NOT closed for /integrations/test-ical and /integrations/test-caldav (362 to 378): both require only user: CurrentUser, no _assert_calendar_connect_access call and no workspace_id parameter at all, one line above the connect endpoints that DO gate on it. Any authenticated user on the platform, regardless of workspace membership, can use either as a one-shot outbound-request primitive: /integrations/test-ical makes the Labs backend issue an HTTPS GET to an attacker-chosen URL carrying attacker-chosen bearer/basic/query credentials, and /integrations/test-caldav opens an attacker-chosen CalDAV connection; neither validates the target host or scheme. auth_type is validated against the FeedAuthType enum only on the SAVE endpoint (connect_ical_feed, 394 to 395) - test_ical_feed passes body.auth_type straight into test_feed with no such check (362 to 371).
Credentials at rest: the four columns under Data model, Fernet-encrypted with one process-wide cipher; losing the key makes every stored credential permanently undecryptable, there is no re-encryption or key-rotation path anywhere in this recipe. Rate limits: none dedicated to any route here, including the two unauthenticated-outbound-request test endpoints above. Attacker-controlled inputs, all behind ordinary session auth: feed_url/caldav_url (free-form string, no scheme or host allow-list on either the test or the save path - the fetch always happens from the Labs backend, an SSRF-shaped surface); auth_type/auth_value/username/password (used to build outbound request auth, encrypted only on the save path); account_email/account_name (free text, stored and displayed, never itself used as a credential).
Tests
Read in full, not executed live for this recipe (the shared scratch Postgres on :5434 was not migrated or written to; every count below is grep -c "^def test_", not a pytest report):
test_booking_g3_workspace_tenant_integration_endpoints_ical_caldav_connect_access_control.py(542 lines, 31def test_, 0@pytest.mark.parametrize): FastAPITestClientagainstapp_fastapi:appwithdependency_overridesfor auth and DB, followingtests/v2/test_f1_scribe_leak.py's pattern per its own docstring (11 to 13). Covers exactly this recipe's surface:_has_workspace_access(owner/member/outsider/inactive-member, 4 tests);encrypt_token/decrypt_tokenround-trip and non-determinism (2 tests);get_primary_workspace_integrationprovider-preference and active-filter (4 tests);GET /workspace/integration(s)withcross_tenant_clientasserting 403 (tenant B against tenant A's workspace);test_connect_ical_requires_admin,test_connect_ical_400_invalid_auth_type,test_connect_ical_encrypts_auth_value,test_connect_caldav_requires_admin,test_connect_caldav_encrypts_password.test_booking_g2_admin_platform_integration_meeting_type_blocked_time_booking_management.py(650 lines, 44def test_): covers the admin half of this recipe -/admin/integration,/integrations(list),/admin/disconnect- plus meeting-type/blocked-time/booking management that is out of this recipe's scope. This recipe's own tests:test_disconnect_platform_404_when_none,test_disconnect_platform_deactivates,test_disconnect_platform_requires_admin,test_service_disconnect_wipes_tokens_and_returns_true(asserts exactlyaccess_token_encrypted==""andrefresh_token_encrypted is None; asserts nothing about an iCal/CalDAV row's credential columns),test_service_disconnect_unknown_returns_false, and the fourtest_service_get_platform_integration_*tests. Both this file and G3 patchapp_fastapi.init_db = lambda: Nonebefore constructing anyTestClient(40 to 57 in G2), because the real app lifespan'sinit_db()drops the transactional test schema mid-session.test_f4_oauth_encryption.py(135 lines, 9def test_, named as a source file for this task): read in full, it does not test this recipe. Its own module docstring (1 to 27) states its target isservices/security/oauth_token_crypto.py, the separate encryption path for Meta / Google Ads / LinkedIn ad-platform tokens; its imports (line 37) confirm this. It shares no code, no Fernet key, and no table withCalendarIntegrationService. This is a genuine gap in the task's own source list, stated plainly rather than papered over: no test file in this repo hermetically exercisesCalendarIntegrationService.encrypt_token/decrypt_tokenin isolation from a live TestClient request; that coverage exists only inside G3'stest_encrypt_decrypt_round_trip/test_encrypt_token_nondeterministicand G2's disconnect assertions above.
What a new app adds and this recipe's own source lacks: no test asserts that disconnecting an iCal or CalDAV integration clears (or deliberately preserves) its own credential column, only that it clears the two OAuth columns (see Gotchas); no test hits /integrations/test-ical or /integrations/test-caldav with a cross-tenant or non-member caller to confirm the missing access check (Security & tenancy) is intentional rather than an oversight.
Deploy & ops
Config keys: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_CALENDAR_REDIRECT_URI (settings fields, core/config.py 619 to 624); CALENDAR_ENCRYPTION_KEY (not a settings field, raw os.getenv, calendar_integration_service.py 90 to 93); ENVIRONMENT (core/config.py 30, default "development", gates whether a missing key auto-creates a local file or raises).
Migration: one revision in this recipe's scope, ical_caldav_001 (migrations/versions/20260427b_add_ical_caldav.py, down-revision content_review_001) - adds every iCal/CalDAV column, drops access_token_encrypted's NOT NULL, drops the (workspace_id, provider) unique constraint (all three checked idempotently via inspect() before altering, 30 to 82). The base calendar_integrations table (OAuth columns, the id/workspace_id/provider shape) predates this recipe's source list - it ships in the frozen baseline_prod_schema_001.py snapshot, not a migration cited here. Downgrade (84 to 101) re-adds the dropped unique constraint and unconditionally re-tightens access_token_encrypted to NOT NULL; both connect_ical_feed and connect_caldav insert rows with access_token_encrypted=None, so running this downgrade after either endpoint has ever been used fails the NOT NULL alter unless those rows are cleaned up by hand first - the migration's own comment only warns about deduping for the unique constraint, not about this.
No scheduler job belongs to this recipe. ical_provider.py's module-level _FEED_CACHE dict (line 110) is an in-process, unbounded-lifetime cache of raw .ics text keyed by integration id; nothing evicts it and a process restart is what clears it (fresh fetch on next read). No Argus-style check is defined specifically for this recipe; the general assertion available is that GET /admin/integration and GET /workspace/integration answer without a 5xx. Rollback of the code alone needs no data migration; a disconnected row (is_active=False) is never deleted by any path in this recipe, so a code rollback does not need to unwind data.
Gotchas
- 2026-09-12 (measured at 62ba4fc,
disconnect_integration,calendar_integration_service.py459 to 472): disconnect clears onlyaccess_token_encrypted/refresh_token_encrypted. An iCal integration'sfeed_auth_value_encrypted(plus its plaintextfeed_auth_username/ical_feed_url) and a CalDAV integration'scaldav_password_encryptedare left in the row, still Fernet-encrypted but still present, after disconnect.is_active=Falsehides the row from every lookup helper this recipe covers, but the credential itself is not removed. No test in any of the three files this recipe was measured against checks an iCal or CalDAV row's credential columns post-disconnect. - 2026-09-12 (measured,
calendar_router.py362 to 378):/integrations/test-icaland/integrations/test-caldavrequireCurrentUseronly, no_assert_calendar_connect_accesscall and noworkspace_id- one line above the real connect endpoints that DO gate on it. Any authenticated user can use either as an outbound-request oracle against an arbitrary URL/CalDAV server with attacker-supplied credentials, with no host allow-list. - 2026-09-12 (measured,
_get_or_create_key,calendar_integration_service.py78 to 112):CALENDAR_ENCRYPTION_KEYis read via rawos.getenv, not throughsettings/pydantic like every neighbouring credential (GOOGLE_CLIENT_SECRETis aSecretStrfield). If the env var is unset AND a.calendar_encryption_keyfile already exists on disk, that file is used regardless ofENVIRONMENT- the production-onlyraise ValueError(109 to 112) is reached only when BOTH the env var and the file are absent. A key file copied from a dev checkout into a prod image would be used silently, with no warning distinguishing it from a properly-provisioned production key. - 2026-09-12 (measured,
handle_oauth_callback, 232 to 257): the existing-integration lookup is(workspace_id, provider='google')only, not scoped by which user completes the flow. A second person re-running OAuth for the same scope (platform, or the same workspace) overwrites the first person's tokens on the same row;user_idis silently reassigned to whoever finished OAuth most recently. - 2026-09-12 (measured,
oauth_callback, 866 to 898): the callback trusts an optional?user_id=query param verbatim, with no signature, and falls back to "the firstis_adminuser" if it's absent; nothing cross-checks it against the encryptedstate, which carries onlyworkspace_id. Inherent to a browser-redirect OAuth callback (the backend cannot control what query params Google's redirect carries) and predates this recipe, not a regression to fix here, but worth knowing before trustinguser_idas an audit trail. - 2026-09-12 (measured,
20260427b_add_ical_caldav.pydowngrade, 84 to 101): downgrading this migration after any iCal or CalDAV row has ever been created fails on the unconditionalaccess_token_encryptedNOT NULLre-tightening, since both connect endpoints insert that column asNone. The migration's own comment warns about deduplicating for the unique constraint it also re-adds, but says nothing about this.
Reference implementation
Paths relative to the repo root; all at main 62ba4fc41009b454e1c033e5036c7e8577db2061 unless noted.
alchemy_labs/backend/services/calendar_integration_service.py(1716 lines): module docstring (1 to 11);_get_or_create_key(78 to 112);_validate_google_config(114 to 135);encrypt_token/decrypt_token(137 to 143);get_oauth_authorization_url(149 to 188);handle_oauth_callback(190 to 280);_create_default_meeting_types(286 to 367, cited for context - only its call sites at 254 to 255/277 to 278 are in scope);get_credentials/get_calendar_service(369 to 407, out of scope, cited for context);get_platform_integration/get_workspace_integration/get_primary_workspace_integration(413 to 457);disconnect_integration(459 to 472).alchemy_labs/backend/api/routers/calendar_router.py(2485 lines): schemasIntegrationResponse/ConnectRequest/ICalFeedConnect/CalDAVConnect/FeedTestRequest/CalDAVTestRequest(45 to 66, 326 to 360);/admin/integration(249 to 270);/admin/connect(273 to 302);/admin/disconnect(305 to 319);/integrations/test-ical(362 to 371);/integrations/test-caldav(374 to 378);/integrations/ical(381 to 421);/integrations/caldav(424 to 452);/integrationslist (455 to 468);/integrations/{id}delete (471 to 495);/callback(866 to 952);_has_workspace_access(959 to 979);_assert_calendar_connect_access(982 to 998);_integration_to_response(1001 to 1014);/workspace/integration(1017 to 1027);/workspace/integrations(1030 to 1054);/workspace/connect(1056 to 1084).alchemy_labs/backend/models/calendar_integration_model.py(439 lines):CalendarProvider/FeedAuthTypeenums (17 to 31);CalendarIntegration(82 to 155, all columns in Data model,is_platform_levelproperty 151 to 154).alchemy_labs/backend/services/calendar/ical_provider.py(307 lines, whole file in scope):_build_request(61 to 96);fetch_feed_textand_FEED_CACHE(107 to 157);test_feed(265 to 307).alchemy_labs/backend/services/calendar/caldav_provider.py(335 lines):_decrypt_password(31 to 35);_open_calendar(38 to 73, used by the write path, out of this recipe's scope, cited for the connectivity-test's own calendar-open logic);test_connection(305 to 329).alchemy_labs/backend/migrations/versions/20260427b_add_ical_caldav.py(whole file, ~101 lines):upgrade(39 to 81);downgrade(84 to 101).alchemy_labs/backend/core/config.py:GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET/GOOGLE_CALENDAR_REDIRECT_URI(619 to 624);ENVIRONMENT(30).alchemy_labs/backend/app_fastapi.py: the router mount (845); the globalHTTPExceptionhandler (724 to 725).- Tests as listed under Tests:
alchemy_labs/backend/tests/v2/coverage/test_booking_g3_workspace_tenant_integration_endpoints_ical_caldav_connect_access_control.py,test_booking_g2_admin_platform_integration_meeting_type_blocked_time_booking_management.py,alchemy_labs/backend/tests/v2/test_f4_oauth_encryption.py(the last does not test this recipe, see Tests).
Deviations of the reference from an ideal implementation, all dated 2026-09-12: disconnect leaves the iCal/CalDAV credential columns populated (Gotchas); the two connectivity-test endpoints carry no tenancy check at all; the OAuth callback trusts an unsigned user_id query param; the encryption key can silently fall back to a stale on-disk file in production; no dedicated hermetic test targets CalendarIntegrationService's own encrypt/decrypt functions outside a live TestClient request.
Acceptance checks
Run from alchemy_labs/backend at 62ba4fc. These are static/structural checks (grep against the pinned commit), not a pytest run - this recipe's Tests section states plainly that the suites were read, not executed, for this measurement.
sed -n '459,472p' services/calendar_integration_service.pyshows exactly two assignments to a credential-shaped column (access_token_encrypted,refresh_token_encrypted) insidedisconnect_integration, confirming Gotcha 1 (nofeed_auth_value_encryptedorcaldav_password_encryptedwrite).sed -n '362,379p' api/routers/calendar_router.pyshowstest_ical_feed/test_caldav_connectiondeclared with onlyuser: CurrentUseras a dependency and no call to_assert_calendar_connect_access, confirming Gotcha 2.grep -n "CALENDAR_ENCRYPTION_KEY" core/config.pyreturns exactly one line (1123), insideSTONE_TOKEN_ENCRYPTION_KEY's own description warning callers not to reuse it, not asettingsfield of its own;grep -n "env_key = os.getenv" services/calendar_integration_service.pyreturns line90, confirming the calendar key is read directly, bypassingsettingsentirely.grep -c "^def test_" tests/v2/coverage/test_booking_g3_workspace_tenant_integration_endpoints_ical_caldav_connect_access_control.pyprints31; the same on the G2 file prints44.sed -n '1,27p' tests/v2/test_f4_oauth_encryption.pyshows the module docstring namingservices/security/oauth_token_crypto.pyas its target, notcalendar_integration_service.py, confirming the Tests-section gap.grep -n "down_revision" migrations/versions/20260427b_add_ical_caldav.pyprintsdown_revision = 'content_review_001'.grep -n "op.alter_column('calendar_integrations', 'access_token_encrypted', nullable=False)" migrations/versions/20260427b_add_ical_caldav.pymatches insidedowngrade(), confirming the rollback hazard in Gotchas.grep -n "surfaces=LABS_SURFACES" app_fastapi.py | grep calendar_routermatches line845, confirming this router carries nosurface_exemptand no feature flag.
Content synced from the prima-modules repository at 0df2ec8 on 2026-09-14. The recipe file is the source; this page is a rendering of it.