T17: GitLab connect (OAuth + read-only PAT) #15
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "t17-gitlab-connect"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
T17: GitLab connect (OAuth + refresh, read-only PAT fallback)
Stacked on #14 (
t15-mcp-tools) → … → #1. Merge those first.What
src/lib/gitlab.ts:read_api read_repository+ PKCE S256; code exchange with HTTP Basic client auth; refresh viagitlabRefresher. GitLab rotates refresh tokens, and the T4 token store handles that safely.invalid_grantmaps toRefreshRejectedError, which becomesreauth_required./personal_access_tokens/self: the token must be active and haveread_api, and every scope must be read-only (read_api,read_repository,read_user,read_registry). Tokens withapi,write_repository,sudoand similar are refused, so we never store a GitLab token that can write.redirect: "manual", and upstream error bodies are never surfaced.src/server/gitlab-connect.ts:GET /auth/gitlab/start: needs the website session.state+ PKCE verifier + user id + expiry are sealed with the AES-GCM keyring in aPath=/auth/gitlab; HttpOnly; SameSite=Lax; Max-Age=600cookie (Secureon https).GET /auth/gitlab/callback(the registered redirect URI): decrypts the cookie and checks expiry,state(constant-time) and that it's the same user as the session, which blocks binding someone else's GitLab account. Then it exchanges the code, binds the GitLab user, saves the encrypted connection, and clears the cookie. Every failure redirects to/connectors?gitlab=error&reason=<code>with no upstream text.POST /api/connections/gitlab/pat(HTML form): session plus a same-originOrigincheck, token format check, validation, then encrypted storage with the PAT's expiry. An expired PAT givesreauth_requiredinstead of a silent failure.src/server/services.ts: per-isolate D1, keyring and a token store with both FIT and GitLab refreshers. The GitLab refresher is only registered when OAuth is configured. Now used by MCP as well./connectorspage (minimal, en/cs; T20 builds the full screen): FIT and GitLab status (account, oauth/pat), "Connect GitLab" when OAuth is configured, and the PAT fallback form. The form is a password input linking to GitLab's token page with the name and read-only scopes pre-filled. It also gives the/connectorslinks in error messages a real page..dev.vars.examplegets an optionalGITLAB_BASE_URLfor other GitLab instances.Probed
gitlab.fit.cvut.czOIDC discovery shows S256 PKCE,
client_secret_basic/client_secret_post, and theread_api/read_repositoryscopes. A bogus refresh returns400 invalid_grant.Verified
bun run lint && bun run typecheck && bun run test: 209 tests, 17 of them for GitLab.invalid_grantwithout leaking, PAT accept/insufficient/write scopes/inactive/unknown/no-expiry.bun run dev+ curl: anonymous/connectors,/auth/gitlab/startand the PAT POST redirect to/login?next=/connectors; the callback without a session gives?gitlab=error&reason=signed_out./connectorsafter the browser login.Astra review (gpt-6-astra)
"No concrete blocking issues found." It confirmed that state and PKCE are bound to the signed-in user, PATs require same-origin and read-only scopes, storage is encrypted, and redirects use configured destinations.
🤖 Generated with Claude Code