Repository navigation
Conversation
A Cloudflare Worker (Hono, jose, D1) in access-site/ that acts as the OIDC provider for Google Workspace's custom OIDC SSO profile, keyed on the numeric GitHub user ID, and lets eligible maintainers create or link their own @modelcontextprotocol.io account. Eligibility comes from the same provisionUser rule Pulumi uses. A nightly job refreshes the account cache and suspends accounts of people who left eligible teams (report-only by default, capped per run, fail-safe on lookup errors). Also: - scripts/backfill-github-ids.ts: stamp existing accounts with GitHub IDs - scripts/sync-team-groups.ts + workflow: mirror GitHub teams as Google groups - src/google.ts: retainOnDelete on Workspace users (suspend, never delete) - src/config/utils.ts: githubTeamsFor, shared by the Worker and the sync - .github/workflows/access-site.yml: test on PR, migrate and deploy on main Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EwaAHtQd625Lwv121Y6PTn
Pulumi PreviewClick to expand preview output |
Account creations, links, suspensions and the nightly report are now JSON log lines (Workers Logs enabled in wrangler.toml) instead of a D1 audit_log table and an optional Discord webhook. Also declare @types/node in access-site so the tests typecheck in a clean CI checkout without the root node_modules. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EwaAHtQd625Lwv121Y6PTn
…of truth Per Infra WG discussion, account creation and mappings stay infrastructure as code. The Worker now only signs people in: it maps the GitHub login to the googleEmailPrefix in users.ts (same provisionUser rule as Pulumi) and issues the OIDC ID token Google needs. It holds no Google credentials. Removed: self-service account page, Directory API client, D1 account cache, nightly offboarding, backfill script, team-group sync, and the src/google.ts / src/config/utils.ts changes. D1 keeps only short-lived pending requests and single-use codes. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EwaAHtQd625Lwv121Y6PTn
On merge to main, scripts/deploy.sh finds or creates the D1 database,
applies migrations and deploys the Worker, uploading secrets from the
production environment. Non-secret settings (account, GitHub App client
ID, Google redirect URI) are committed in wrangler.toml. The deploy job
skips with a notice until the secrets exist, so merging early is safe.
Replaces the manual generate-keys script; signing key rotation now goes
through the ACCESS_SITE_OIDC_PREVIOUS_JWK secret, and the Worker ignores a
retired "{}" value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EwaAHtQd625Lwv121Y6PTn
Node: CI on Node 24 (current LTS), engines >=24, @types/node 24. Security review fixes: - Pin GitHub logins to GitHub user IDs on first sign-in; refuse a login that comes back from a different account (freed and re-registered name) - Per-IP rate limit on /authorize and /login (Workers rate limiting binding) so anonymous traffic can't exhaust D1 writes; clean up expired rows on 5% of sign-ins instead of every one - Reject a code_verifier when no PKCE challenge was sent (RFC 9700) - Form-decode Basic client credentials (RFC 6749 2.3.1) - Stamp auth_time at GitHub sign-in, not at token exchange - Revoke the GitHub user token right after reading the user; clear the OAuth state cookie - CI: pin actions to commit SHAs, don't persist Git credentials, expose deploy secrets only to the deploy step (never to npm ci) - Override dev-only undici to the patched 7.30.1 (npm audit: 0) - Test that users.ts has no case-insensitive duplicate logins or prefixes Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EwaAHtQd625Lwv121Y6PTn
Google's custom OIDC profile needs a change password URL; point it at GitHub's password and authentication settings. Replace the 'verify on first deploy' notes with what Google documents: the redirect URI is per-profile (set by PR after setup), and PKCE is handled either way. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EwaAHtQd625Lwv121Y6PTn
PILOT_GITHUB_LOGINS in wrangler.toml limits sign-in to the listed GitHub logins; it starts as just "sambhav" ([email protected] via users.ts). A test checks every pilot login maps to an account. Remove the line by PR to open sign-in to everyone in users.ts. Setup is reordered so each secret is generated by the person who uses it: the OIDC client secret is created by whoever sets up the Google SSO profile, and goes straight into GitHub secrets. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EwaAHtQd625Lwv121Y6PTn
|
@dsp-ant this is ready for review. Three things need you, all in the Setup → @dsp-ant section of the description:
Sign-in starts as a pilot for me only ( The PR can merge before any of this; the deploy job skips until the secrets exist. Generated by Claude Code |
Automated review (Claude-generated)
Severity key: 🔴 High · 🟠 Medium · 🟡 Low · ⚪ Nit 🔴 High1. 2. Identity is trust-on-first-use of a mutable GitHub login — 🟠 Medium3. GitHub error callback causes a redirect loop — 4. OAuth 5. Key-rotation procedure can break verification for up to the JWKS cache TTL — 6. Deploy gate doesn't require the GitHub client secret — 7. Test fixtures are derived from live 🟡 Low8. 9. Eligibility predicate is re-implemented instead of shared — 10. 11. 12. ⚪ Nit13. Hand-rolled base64url — 14. Generated by Claude Code |
Bring the PR branch up to date with main. Verify configured GitHub IDs before issuing Workspace credentials, bind the OAuth callback to the pending sign-in, and address deployment, key rotation and test coverage from review.
- Replace the OIDC_PREVIOUS_JWK and OIDC_NEXT_JWK slots with one OIDC_ROTATION_JWKS JWK Set, which removes a secret, the per-slot parsing and the jq conditionals in deploy.sh - Check for duplicate GitHub logins in validate-config instead of a Worker test that read the live users.ts - Use nowSeconds() for auth_time and fix stale vitest config comments Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_014dNQoC6mUhYz5VKJL2QwiJ
|
@dsp-ant thanks for the review. All 14 items are addressed in bf5bde6, and 72b7dcf simplifies the result:
Worker: 23 tests and typecheck pass. Repo: format, config validation and 79 tests pass. Generated by Claude Code |
Record the IDs for the current GitHub logins. Verify ownership with each member before expanding the Workspace sign-in pilot.
|
Ready for review. The review fixes are in, and the access-site, repository CI, and Pulumi preview checks all passed. I also backfilled the numeric GitHub IDs for all 139 configured usernames in The Workspace SSO pilot is still limited to |
Summary
Maintainers sign in to MCP Google Workspace with GitHub instead of a Google password.
Google stays the IdP for everything. Workspace's built-in "SSO with a third-party IdP" feature hands off only the password step:
App → Google → access.modelcontextprotocol.io → GitHub
access-site/is a small Cloudflare Worker (Hono, jose, D1) that acts as the OIDC provider for Google's custom OIDC SSO profile. It signs the user in with GitHub and returns an ID token whose subject is their numeric GitHub user ID and whose email is their Workspace address.Accounts stay infrastructure as code. The address comes from
users.ts(googleEmailPrefix). A member can use a provisioned or existing Workspace account once their verified numericgithubUserIdis recorded in the same config. The Worker bundlessrc/configand redeploys on every config change, so removing someone fromusers.tsstops their sign-in on the next deploy. It holds no Google credentials and creates nothing in Google. GitHub users without an account get a page explaining how to request one.Provisioning is in GitHub Actions too: on merge to
main,access-site/scripts/deploy.shfinds or creates the D1 database, applies migrations and deploys the Worker with secrets from theproductionenvironment. Non-secret settings are committed inaccess-site/wrangler.toml. Until all four required secrets exist, the deploy job is skipped with a notice, so this can merge before setup.Security
users.tsbefore any token is issued. Recycled logins are refused from the first attempt.DELETE ... RETURNING), stored hashed, 2 minute TTL; PKCE enforced when sent and downgrades rejected (RFC 9700); exact redirect URI match; client credentials form-decoded (RFC 6749) and compared in constant time/authorizeand/logingithubUserIdandgoogleEmailPrefixrequire core maintainer review through CODEOWNERS.Testing
Rollout
Sign-in starts as a pilot for one person:
PILOT_GITHUB_LOGINS = "sambhav"inwrangler.tomlmeans only @sambhav ([email protected]) can get a token, even if the Google SSO profile is assigned more widely. A test checks that each pilot login maps to an account with a verified GitHub ID. Before expanding the pilot, verify each additional member’s GitHub ID in a reviewed PR and include only those users in the Google SSO profile.Setup
Secrets go straight from the command line into GitHub secrets (needs
opensslandgh), so none pass between people. This PR can merge first: the deploy job skips until the secrets exist.@sambhav
modelcontextprotocol.io. Store asCLOUDFLARE_ACCESS_SITE_TOKENin theproductionenvironment.wrangler.toml, merge, and check https://access.modelcontextprotocol.io/.well-known/openid-configuration.wrangler.toml, then test sign-in.@dsp-ant (org owner and Workspace super admin)
https://access.modelcontextprotocol.iohttps://access.modelcontextprotocol.io/login; leave "Expire user authorization tokens" on; device flow offgh secret set ACCESS_SITE_GITHUB_CLIENT_SECRET --env production -R modelcontextprotocol/accesshttps://access.modelcontextprotocol.iogoogle-workspace; client secret fromoidc-client-secret.txt(delete the file after)https://github.com/settings/securityBefore assigning the profile to Model Context Protocol and removing
PILOT_GITHUB_LOGINS, verify GitHub IDs and Workspace mappings for everyone affected, including members with existing Workspace accounts.Runbook, including key rotation:
access-site/README.md.https://claude.ai/code/session_01EwaAHtQd625Lwv121Y6PTn
Generated by Claude Code