Repository navigation
Scheduling: customers' own OAuth apps - #339
Merged
Merged
Conversation
Scale and Enterprise customers can connect hosts' calendars through their own Google, Microsoft or Zoom app, so the consent screen shows their brand. - scheduling_oauth_app: one per environment and provider, with the secret encrypted and covered by reencryptWebhookSecrets. Connections and consents are pinned to the app that issued their tokens, so adding an app never disturbs hosts already connected. - A customer's page on their own domain can be the redirect URI and forward to the existing callback, since Google verifies redirect domains as the app owner's. completion_url says where the browser lands afterwards. - A refused client secret (invalid_client, deleted_client) is now transient, so it's one failing app with one webhook and one owner email rather than every host marked needs_reauth. This also covers coss.com's own apps. - Zoom deauthorization events are per app (/scheduling/zoom/events/:appId) and scoped to that app's connections, so a customer holding their own event token can't reach other organizations'. - Plan gate schedulingOauthApps on creating only; rotating and deleting always work. Self-serve downgrades are refused while apps exist. - API, SDK, dashboard page, docs, and the hosted page's consent copy. The migration is generated after merging main.
# Conflicts: # AGENTS.md # apps/api/openapi.json # apps/site/src/lib/changelog.ts # apps/web/src/app/[orgSlug]/[projectSlug]/[envSlug]/scheduling/actions.ts # apps/web/src/app/[orgSlug]/[projectSlug]/[envSlug]/scheduling/hosts/page.tsx # apps/web/src/lib/billing-preflight.ts # packages/billing/src/catalog/entitlements.ts # packages/billing/src/catalog/plans.ts
- Updating an app re-reads its credentials under a row lock, so a Secret Token saved while a new client secret is being tried isn't overwritten. - A successful refresh or consent always tries to end an outage, fenced to the credentials that worked, rather than trusting the status it read beforehand. - The expiry warning walks every app in the window with a cursor: those already warned stay in it for two weeks, and could starve the ones behind them.
# Conflicts: # AGENTS.md # apps/api/openapi.json # apps/api/src/middleware/request-log.ts # apps/api/src/routes/scheduling-client.ts # apps/api/src/routes/scheduling.ts # apps/api/src/webhook-events.ts # apps/api/test/app.test.ts # apps/api/test/contract-types.ts # apps/api/test/sdk-contract.test.ts # apps/scheduling/src/app/h/[sessionId]/types.ts # apps/web/src/app/[orgSlug]/[projectSlug]/[envSlug]/scheduling/actions.ts # packages/db/migrations/meta/0139_snapshot.json # packages/db/migrations/meta/_journal.json # packages/email/src/test-event-schemas.ts # packages/scheduling/AGENTS.md # packages/scheduling/src/schemas.ts # packages/sdk/src/core/routes.generated.ts # packages/sdk/src/index.ts # packages/sdk/src/resources/scheduling.ts # packages/sdk/src/types/scheduling.ts # packages/sdk/src/types/webhook.ts # packages/sdk/src/webhook-event-types.ts # packages/webhooks/src/reencrypt.ts
A Microsoft consent is exchanged at the tenant it started with: the state row now keeps tenant_id alongside the app and redirect URI, so editing the app while a consent is in progress can't send its code to a different authority. The branch's one migration is regenerated with the new column.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Scale and Enterprise customers can connect hosts' calendars through their own Google, Microsoft or Zoom app, so the consent screen shows their brand instead of coss.com's.
scheduling_oauth_app: one per environment and provider, managed from Scheduling → OAuth apps andcoss.scheduling.oauthApps. The secret is encrypted withWEBHOOK_SECRETS_KEYand covered byreencryptWebhookSecrets. Never returned.oauth_app_id, null for coss.com's), so adding an app disturbs nobody already connected. A consent pins its app, redirect URI and Microsoft tenant when it starts.api.coss.com. Soredirect_urican be a page on their own domain that forwardscodeandstateto the existing callback.completion_urlsays where the browser lands afterwards.invalid_clientanddeleted_clientare now transient, so the app goesfailingwith one webhook and one owner email, and no host is marked or emailed. This also applies to coss.com's own apps, where it logs to Sentry. Calendars go stale andcalendarVerdicthides hosts as usual. New secrets are tried before they replace the old one.POST /scheduling/zoom/events/:appId, its own Secret Token) and scoped to that app's connections. The old handler reached every organization's, which a customer holding their own token must never be able to do.schedulingOauthAppson Scale and Enterprise, checked when creating an app, in sandbox too. Rotating and deleting always work. Self-serve downgrades are refused while apps exist; if a contract just ends, existing apps keep working.privacy_url, and Zoom's agenda marker no longer says coss.com (existing meetings are still found, by hash).Migration
0140is additive: a new table, nullable columns, and no backfill.Testing
bun run verifyis green on this commit.packages/scheduling/test/oauth-apps.test.tsandapps/api/test/scheduling-oauth-apps.test.ts. They cover pinning, per-environment isolation, outages and recovery, the races below, Zoom tenant isolation, the real plan gate, re-encryption and environment deletion.apps/sitebuilds, so the new guide's Twoslash samples compile.Not tested against real provider apps
The fakes can't prove these, and only one live call was made: real Google rejected a made-up client as
invalid_client.ZOOM_APP_SCOPES(also in the docs) was written from memory, since the repo had no list.Before telling customers
apps/site/content/legal/privacy.mdxdescribes coss.com's Google Limited Use. It's unchanged here, and needs a legal read for customer-owned apps, along with whether the DPA needs a line.Review
Four Codex rounds against
origin/main, withmainmerged in between (migration regenerated each time).No findings were rejected.
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.