#769

[FEATURE REQUEST] Add Gitlab Support

Openwillsamuopened 7mo ago

Feature Request: GitLab Integration Support

Summary

Add GitLab as a supported git provider alongside GitHub, enabling users to connect GitLab repositories, clone private projects, push branches, and create merge requests from the agent workflow. GitLab access tokens (personal, group, and project) must be supported as a first-class authentication method alongside OAuth, allowing granular permission scoping that OAuth alone cannot provide.

Motivation

Open Agents currently only supports GitHub as a git provider. The entire repo access, auth, and PR/push flow is tightly coupled to GitHub App primitives (installation tokens, GitHub OAuth, GitHub webhooks). This excludes a significant portion of teams — particularly enterprises and self-hosted shops — that use GitLab as their primary SCM.

Since the project is explicitly designed to be “forked and adapted, not treated as a black box,” supporting GitLab would meaningfully expand the audience and reinforce that goal. Many organizations evaluating cloud coding agents run GitLab (self-managed or SaaS) and cannot adopt Open Agents without building their own integration from scratch.

Why Access Tokens Are Required, Not Optional

GitLab’s OAuth application model grants broad scopes (api, read_repository, write_repository) that apply uniformly across every project the authenticated user can access. There is no mechanism in GitLab OAuth to restrict a token’s reach to a single project or group the way a GitHub App installation scopes access to selected repositories.

GitLab access tokens solve this directly:

  • Project Access Tokens are scoped to a single repository with an explicit role (Reporter, Developer, Maintainer) and fine-grained scopes (read_repository, write_repository, api). The agent never touches anything outside the target project.
  • Group Access Tokens are scoped to a group and its subgroups/projects — appropriate for teams that want the agent to operate across related repositories without granting org-wide access.
  • Personal Access Tokens (PATs) provide user-level access with selectable scopes. They are the most common auth method for CI/CD, automation, and self-managed GitLab instances where OAuth apps are not configured.

For security-conscious organizations, OAuth-only support would be a non-starter. The ability to hand the agent a project-scoped token with write_repository and nothing else is a hard requirement for adoption in regulated environments.

Current GitHub-Specific Surface Area

Based on the current codebase structure and README, the following are tightly coupled to GitHub:

Authentication & OAuth

  • GitHub App user authorization flow (NEXT_PUBLIC_GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET)
  • OAuth callback route at /api/github/app/callback
  • No separation between “git provider auth” and “GitHub auth” — the two are fused

Repository Access

  • GitHub App installation tokens for cloning private repos
  • Installation-based repo/org access model (GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY)
  • NEXT_PUBLIC_GITHUB_APP_SLUG for app installation flow

Code Operations

  • Auto-commit, push, and branch creation assumes GitHub remote
  • PR creation uses GitHub API (not generic git forge API)
  • GITHUB_WEBHOOK_SECRET for inbound event handling

Environment Variables

Six required env vars are GitHub-specific:

NEXT_PUBLIC_GITHUB_CLIENT_ID
GITHUB_CLIENT_SECRET
GITHUB_APP_ID
GITHUB_APP_PRIVATE_KEY
NEXT_PUBLIC_GITHUB_APP_SLUG
GITHUB_WEBHOOK_SECRET

Proposed Approach

1. Dual Auth Model: OAuth + Access Tokens

GitLab integration should support two authentication paths as equal first-class options:

Path A — OAuth Application (interactive, user-scoped) Standard OAuth 2.0 with PKCE. The user authorizes via browser redirect, and Open Agents receives a token with the granted scopes. Best for individual developers connecting their own account.

Path B — Access Token (static, scope-controlled) The user provides a pre-created access token (personal, group, or project-level). No browser redirect required. The token is encrypted at rest and used directly for API calls and git operations. Best for teams, CI-like workflows, and environments where granular permission control is required.

The two paths converge on the same downstream interface — once authenticated, the provider abstraction doesn’t care whether the credential came from OAuth or a pasted token.

Access Token Hierarchy

Token TypeScopeBest For
Project Access TokenSingle repository, explicit role + scopesAgent restricted to one repo. Tightest blast radius.
Group Access TokenAll projects within a group/subgroup, explicit role + scopesAgent operating across related repos in a team.
Personal Access TokenAll projects the user can access, selectable scopesIndividual developer use, self-managed instances without OAuth apps.

All three types support the same scope flags (read_repository, write_repository, api, etc.) and are validated the same way via the GitLab API.

Token Validation

On token submission, Open Agents should call the GitLab API to validate the token and determine its type and scope:

  • GET /api/v4/personal_access_tokens/self — returns token metadata including scopes, expiry, and whether it’s active
  • If that returns 404/403, the token may be a project or group access token — validate by attempting GET /api/v4/projects or GET /api/v4/groups with the token and inspecting accessible resources
  • Display the resolved token type, scopes, and accessible projects/groups to the user before saving

2. Git Provider Abstraction Layer

Create an interface that encapsulates the operations both GitHub and GitLab need to support. The credential type is abstracted behind a union so the provider implementation handles the OAuth-vs-token difference internally:

type GitCredential =
  | { type: 'oauth'; accessToken: string; refreshToken?: string }
  | { type: 'access_token'; token: string; tokenLevel: 'personal' | 'group' | 'project' };

interface GitProvider {
  readonly name: 'github' | 'gitlab';

  // Auth — OAuth flow (optional for token-based auth)
  getAuthorizationUrl?(state: string): string;
  handleOAuthCallback?(code: string): Promise<GitCredential>;
  refreshToken?(refreshToken: string): Promise<GitCredential>;

  // Auth — Token flow
  validateToken?(token: string, baseUrl: string): Promise<TokenValidationResult>;

  // Repository access
  listRepositories(credential: GitCredential): Promise<Repository[]>;
  cloneRepository(repo: Repository, credential: GitCredential, sandbox: Sandbox): Promise<void>;

  // Code operations
  createBranch(repo: Repository, branchName: string, credential: GitCredential): Promise<void>;
  pushChanges(repo: Repository, branch: string, credential: GitCredential): Promise<void>;
  createMergeRequest(repo: Repository, mr: MergeRequestInput, credential: GitCredential): Promise<MergeRequest>;

  // Webhooks
  verifyWebhook(payload: string, signature: string): boolean;
  parseWebhookEvent(payload: unknown): WebhookEvent;
}

interface TokenValidationResult {
  valid: boolean;
  tokenLevel: 'personal' | 'group' | 'project';
  scopes: string[];
  expiresAt: string | null;
  accessibleProjects?: { id: number; pathWithNamespace: string }[];
  missingScopes?: string[];   // scopes required by Open Agents but not granted
}

The GitHubProvider implementation would only implement the OAuth methods (since GitHub Apps don’t use static tokens in the same way). The GitLabProvider would implement both.

3. Environment Variables

GitLab configuration supports both auth paths:

# Required
GITLAB_BASE_URL              # defaults to https://gitlab.com, overridable for self-managed

# OAuth path (optional — not needed if using token auth only)
GITLAB_CLIENT_ID
GITLAB_CLIENT_SECRET

# Token path (optional — not needed if using OAuth only)
# Tokens are stored per-user in the database, not as env vars.
# But for single-tenant / self-hosted deployments, a default token can be set:
GITLAB_DEFAULT_ACCESS_TOKEN

# Webhooks (optional)
GITLAB_WEBHOOK_SECRET

GITLAB_BASE_URL is critical — unlike GitHub, GitLab is widely self-hosted, so the integration cannot assume gitlab.com. Every API call and git clone URL must be constructed relative to this base.

For multi-user deployments, individual access tokens are stored encrypted in the database per-user (not in env vars). GITLAB_DEFAULT_ACCESS_TOKEN is a convenience for single-tenant or self-hosted setups where one token serves the whole instance.

4. API Route Refactoring

Current routes under /api/github/ should be restructured:

/api/git/[provider]/callback       → OAuth callback (github | gitlab)
/api/git/[provider]/webhook        → Inbound webhooks
/api/git/[provider]/repos          → Repository listing
/api/git/[provider]/token/validate → Token validation + scope check (gitlab only)
/api/git/[provider]/token/connect  → Save encrypted token + link provider (gitlab only)

Or, keep /api/github/ and add /api/gitlab/ in parallel — simpler to ship incrementally but creates more duplication.

5. GitLab-Specific Considerations

ConcernGitHubGitLab Equivalent
App modelGitHub App (installation tokens)OAuth Application or Access Tokens (project/group/personal)
Permission scopingApp installed on selected reposProject token = single repo, Group token = group, PAT = user-wide
PR creationPOST /repos/{owner}/{repo}/pullsPOST /projects/{id}/merge_requests
Repo cloning authInstallation token as passwordOAuth token or access token as password via https://oauth2:{token}@gitlab.example.com/...
Webhook verificationHMAC-SHA256 on X-Hub-Signature-256Secret token on X-Gitlab-Token header (simple string match)
Org/group accessApp installed on orgGroup access token or OAuth scope
Self-hostedGitHub Enterprise Cloud is a separate productGITLAB_BASE_URL overrides all API and git paths
Token expiryInstallation tokens auto-expire (1h)Access tokens have configurable expiry; OAuth tokens use refresh flow

6. Database Schema Changes

The sessions/users tables likely store GitHub-specific identifiers (GitHub user ID, installation ID, etc.). These should be generalized:

  • Add a git_provider column (enum: github, gitlab)
  • Add a git_auth_method column (enum: oauth, access_token)
  • Store provider-specific IDs in a flexible structure (JSONB or separate provider tables)
  • Ensure repo references include provider context (not just owner/repo — GitLab uses numeric project IDs and namespace/project paths)
  • Access tokens stored encrypted at rest, with metadata: token level, scopes, expiry, and the resolved list of accessible projects at time of connection

7. UI Changes

  • Connect GitLab flow in settings, offering two clear paths:
    • “Sign in with GitLab” → OAuth redirect (only available if OAuth app is configured)
    • “Connect with Access Token” → paste token, validate, show resolved scope/projects, save
  • Repository picker should indicate which provider a repo comes from and, for token auth, show which token grants access
  • Token management UI: list connected tokens, show type/scopes/expiry, revoke/replace
  • Session/chat UI referencing “PR” should adapt to “Merge Request” when the repo is GitLab-hosted
  • Warning when a token is approaching expiry

Implementation Phases

Phase 1 — Abstraction (no new functionality) Extract the GitHub-specific code behind the GitProvider interface. Introduce the GitCredential union type. All existing GitHub behavior stays identical, but the coupling is broken.

Phase 2 — GitLab Access Token Auth + Repo Access Implement token validation, encrypted storage, and the “Connect with Access Token” UI flow. Support cloning and reading repos via project/group/personal access tokens. This is the minimum viable GitLab integration and can ship without OAuth support.

Phase 3 — GitLab OAuth (optional path) Add the OAuth flow for users who prefer browser-based auth. This is additive — token auth from Phase 2 continues to work independently.

Phase 4 — Push + Merge Request Creation Implement branch push and MR creation via the GitLab API. Auto-commit and auto-MR work the same as the GitHub flow. Both OAuth and token credentials are supported.

Phase 5 — Webhooks + Self-Managed Hardening Handle inbound GitLab webhooks (push events, MR events). Ensure the full flow works against self-managed GitLab instances with custom base URLs, internal CAs, and network restrictions.

Open Questions

  1. Should provider selection be per-user or per-session? A user might have both GitHub and GitLab repos. Ideally they can connect both providers and select per-session.
  2. Self-managed GitLab TLS: Self-hosted instances may use internal CAs. Should the sandbox trust custom CA bundles?
  3. Token rotation: Project and group access tokens can be rotated via the GitLab API (POST /personal_access_tokens/{id}/rotate). Should Open Agents handle automatic rotation before expiry?
  4. Minimum required scopes: The integration should enforce a minimum scope set (likely read_repository + write_repository + api for full functionality) and degrade gracefully when only read scopes are present (disable push/MR creation, allow clone/read).
  5. Scope: Should this also cover Bitbucket, or is GitLab sufficient for a first pass? A clean abstraction layer would make Bitbucket straightforward to add later.

References

Activity

0

No activity yet.