npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode-gitlab-auth

v3.0.0

Published

GitLab OAuth authentication plugin for OpenCode

Readme

OpenCode GitLab Auth

A GitLab authentication plugin for OpenCode v2 that adds a PKCE OAuth 2.0 method and a self-managed instance URL prompt to OpenCode's built-in gitlab integration.

npm version License: MIT

Upgrading from 2.x? See Upgrading from 2.x below. Version 3.0 targets OpenCode's plugin SDK v2 and is a breaking change from the 2.x line, which targeted @opencode-ai/plugin (v1).

Why this plugin exists

OpenCode v2 already ships a built-in gitlab provider (gitlab-ai-provider) and a gitlab integration with env (GITLAB_TOKEN) and manual API key methods out of the box. This plugin adds what core does not:

  • PKCE OAuth 2.0 — browser-based authentication with automatic token refresh, so you don't need to create or manage a Personal Access Token.
  • Self-managed instance URL support — a form field on both the OAuth and PAT methods lets you point at a self-hosted GitLab instance instead of gitlab.com.

Everything else — credential storage, scheduling token refresh, and wiring instanceUrl/apiKey into the gitlab-ai-provider SDK — is handled by OpenCode core once this plugin registers the method.

Features

🔐 Two authentication methods on the gitlab integration

  • OAuth 2.0 with PKCE — secure browser-based authentication with automatic token refresh, no client secret required.
  • Personal Access Token (PAT) — simple token-based authentication for automation, CI/CD, and headless environments.

🛡️ Security

  • PKCE (Proof Key for Code Exchange) — eliminates the need for a client secret in a desktop/CLI context.
  • State parameter — CSRF protection on the OAuth redirect.
  • Local-only callback — the OAuth redirect is handled by a loopback (127.0.0.1) HTTP server that closes as soon as the callback is received or the flow times out.
  • Credential storage and refresh — owned entirely by OpenCode core; this plugin never reads or writes token files itself.

🌐 Flexible instance support

  • GitLab.com (default)
  • Self-managed GitLab instances, via an instanceUrl field on both auth methods

Table of Contents

Requirements

  • OpenCode v2 (plugin SDK @opencode/plugin ^2.0.0)
  • Node.js 20.0.0 or higher
  • npm 9.0.0 or higher

Installation

Via OpenCode's plugin manager

opencode plugin add opencode-gitlab-auth

Via config

Add the package to plugins in opencode.json/opencode.jsonc:

{
  "$schema": "https://opencode.ai/config.json",
  "plugins": ["opencode-gitlab-auth"],
}

Note the config key is plugins (plural) in OpenCode v2, not plugin.

Local development install

git clone https://gitlab.com/vglafirov/opencode-gitlab-auth.git
cd opencode-gitlab-auth
npm install
npm run build

Point a project's config at the local build instead of npm:

{
  "plugins": ["/absolute/path/to/opencode-gitlab-auth"],
}

Quick Start

1. Configure OpenCode

{
  "plugins": ["opencode-gitlab-auth"],
}

2. (Optional) Use your own OAuth application

The plugin ships with a default OAuth application registered as a public PKCE client with redirect URI http://127.0.0.1:8080/callback, so OAuth works out of the box on gitlab.com. You only need your own application for a self-managed instance, or if you prefer not to rely on the default one.

  1. Go to your instance's User Settings > Applications (on gitlab.com: here)
  2. Create a new application with:
    • Name: OpenCode GitLab Auth
    • Redirect URI: http://127.0.0.1:8080/callback (must match exactly)
    • Confidential: ❌ unchecked
    • Scopes: api
  3. Copy the Application ID (a 64-character hex string)
  4. Make it available to the OpenCode server process. The plugin reads GITLAB_OAUTH_CLIENT_ID from the environment of the server, which in OpenCode v2 is normally a long-lived background service — so a plain export in your shell will not reach it. Persist it on the service instead:
opencode service set env GITLAB_OAUTH_CLIENT_ID <your-application-id>
opencode service restart

Verify it took effect:

opencode service get env GITLAB_OAUTH_CLIENT_ID

"Confidential" must be unchecked. PKCE is designed for public clients and this plugin never sends a client_secret. A confidential application makes GitLab require one, and the token exchange then fails.

A .env file in this repository is not read by the plugin or by OpenCode. Setting GITLAB_OAUTH_CLIENT_ID there has no effect — use opencode service set env (above) or export the variable before launching a foreground/--standalone server.

3. Authenticate

opencode

Then run OpenCode's connect/auth command and choose the gitlab integration. Two methods will be listed:

  • GitLab OAuth — browser-based PKCE flow (this plugin)
  • GitLab Personal Access Token — token-based auth (this plugin adds the instance URL field; the base method ships with OpenCode core)

Authentication Methods

OAuth 2.0 (Recommended)

How it works

  1. You select GitLab OAuth and optionally enter a self-managed instance URL.
  2. The plugin starts a loopback HTTP server on 127.0.0.1:8080 and returns an authorization URL to OpenCode.
  3. OpenCode's UI presents the URL for you to open (or opens it for you, depending on the client).
  4. You approve the request on GitLab; GitLab redirects to the local callback server with an authorization code.
  5. The plugin verifies the state parameter, exchanges the code for tokens using the PKCE verifier, and returns the credential to OpenCode core.
  6. OpenCode core stores the credential and refreshes it automatically before it expires, calling back into this plugin's refresh handler.
sequenceDiagram
    participant User
    participant OpenCode as OpenCode Core
    participant Plugin as This Plugin
    participant Browser
    participant GitLab
    participant LocalServer as Local Callback Server (127.0.0.1:8080)

    User->>OpenCode: Select "GitLab OAuth"
    OpenCode->>Plugin: authorize(answer)
    Plugin->>LocalServer: start()
    Plugin->>OpenCode: { url, instructions }
    OpenCode->>Browser: Open authorization URL
    Browser->>GitLab: Authorization request (PKCE challenge)
    GitLab->>Browser: User approves
    Browser->>LocalServer: Redirect with code + state
    Plugin->>GitLab: POST /oauth/token (code_verifier)
    GitLab->>Plugin: access + refresh tokens
    Plugin->>OpenCode: Credential.OAuth
    OpenCode->>OpenCode: Store credential; schedule refresh

Advantages

  • ✅ No long-lived credential to manage yourself
  • ✅ Automatic token refresh, handled by OpenCode core
  • ✅ Revocable from GitLab settings
  • ✅ PKCE eliminates the need for a client secret

Requirements

  • A GitLab OAuth application with redirect URI http://127.0.0.1:8080/callback (see Quick Start)
  • GITLAB_OAUTH_CLIENT_ID environment variable set to that application's ID
  • A way to open a URL in a browser during authentication

Personal Access Token

How to create a PAT

  1. Go to GitLab User Settings > Access Tokens
  2. Create a token with the api scope and an expiration per your security policy
  3. Copy the token (starts with glpat-)
  4. Enter it, along with an optional self-managed instance URL, when prompted by OpenCode

Advantages

  • ✅ No OAuth app registration needed
  • ✅ Works in headless environments and CI/CD
  • ✅ No browser required

Disadvantages

  • ⚠️ Manual token rotation
  • ⚠️ Long-lived credential
  • ⚠️ Not validated at entry time. Unlike the previous (v1/2.x) implementation, OpenCode v2's key auth method has no hook to test the token against GitLab before saving it. An invalid or expired PAT will only surface as a failure on the first model call. Double-check the token before submitting it.

Configuration

Environment Variables

| Variable | Description | Required | Default | | ------------------------ | -------------------------------------------------------------------------- | --------- | ------------------------------------- | | GITLAB_OAUTH_CLIENT_ID | OAuth application ID | For OAuth | Bundled ID (does not work by default) | | GITLAB_INSTANCE_URL | Default GitLab instance | No | https://gitlab.com | | GITLAB_TOKEN | Read by core's env auth method for gitlab (not managed by this plugin) | No | — |

Credential storage

Credentials are stored and refreshed entirely by OpenCode core, not by this plugin. This plugin only supplies the authorize, refresh, and label callbacks that core invokes; it does not read or write any files (no auth.json, no debug log — both were removed when the v1 auth-loader/token-refresh code was replaced by core's integration/credential system).

Architecture

Project structure

opencode-gitlab-auth/
├── src/
│   ├── index.ts              # Plugin entry point (Plugin.define)
│   ├── instance-url.ts       # Instance URL resolution/validation helpers
│   ├── oauth-flow.ts         # GitLab OAuth token exchange (code + refresh grants)
│   ├── callback-server.ts    # Local node:http server for the OAuth redirect
│   └── pkce.ts                # PKCE utilities (verifier & challenge)
├── dist/                      # Compiled JavaScript (generated)
├── package.json
├── tsconfig.json
└── README.md

Core components

1. Plugin entry point (index.ts)

Default-exports a Plugin.define({ id, setup }) definition. During setup, it registers two methods on the gitlab integration via ctx.integration.transform:

  • An oauth method (authorize, refresh, label) implementing the PKCE flow described above.
  • A key method that adds the instanceUrl form field to the PAT flow OpenCode core already provides.
export default Plugin.define({
  id: 'gitlab-auth',
  async setup(ctx) {
    await ctx.integration.transform((editor) => {
      editor.method.update({
        integrationID: 'gitlab',
        method: {
          /* oauth */
        },
        authorize,
        refresh,
        label,
      });
      editor.method.update({
        integrationID: 'gitlab',
        method: {
          /* key */
        },
      });
    });
  },
});

There is no loader, no token-refresh mutex, and no filesystem access — OpenCode core's credential/integration system replaces all of it. See the V1→V2 plugin migration guide for the general shape of this change.

2. OAuth flow (oauth-flow.ts)

GitLabOAuthFlow performs the two GitLab OAuth token-exchange requests this plugin needs:

class GitLabOAuthFlow {
  constructor(options: { instanceUrl: string; clientId: string });
  async exchangeAuthorizationCode(
    code: string,
    codeVerifier: string,
    redirectUri: string
  ): Promise<OAuthTokens>;
  async exchangeRefreshToken(refreshToken: string): Promise<OAuthTokens>;
}

3. Callback server (callback-server.ts)

A minimal node:http server (no external dependencies) that listens on 127.0.0.1:8080 for the OAuth redirect, parses code/state/error query parameters, renders a success/failure HTML page, and closes itself once the callback is received or the configured timeout elapses. Includes a simple request counter to bound the number of requests it will process before refusing further ones.

class CallbackServer {
  constructor(options?: { port?: number; host?: string; timeout?: number });
  async start(): Promise<void>;
  async waitForCallback(): Promise<{ code: string; state: string }>;
  getPort(): number;
  getCallbackUrl(): string;
  async close(): Promise<void>;
}

4. PKCE utilities (pkce.ts)

function generateSecret(length?: number): string; // random verifier/state
function generateCodeChallengeFromVerifier(verifier: string): string; // SHA-256, base64url

Uses Node's crypto module; no client secret is ever generated or required.

Data flow

flowchart TD
    A[User selects GitLab OAuth] --> B[authorize receives instanceUrl answer]
    B --> C[Generate PKCE verifier, challenge, state]
    C --> D[Start local callback server on :8080]
    D --> E[Return authorization URL + instructions to OpenCode]
    E --> F[User approves on GitLab in browser]
    F --> G[GitLab redirects to local callback server]
    G --> H{state matches?}
    H -->|No| X[Reject: possible CSRF]
    H -->|Yes| I[POST /oauth/token with code_verifier]
    I --> J[Return Credential.OAuth to OpenCode core]
    J --> K[OpenCode core stores credential]
    K --> L[Core calls refresh before expiry]

Security

PKCE

Traditional OAuth requires a client secret, which cannot be safely embedded in a CLI/desktop application. PKCE avoids this: the client generates a random verifier, sends its SHA-256 hash (the "challenge") during authorization, and only reveals the verifier during token exchange — a value never transmitted during the browser redirect and therefore not interceptable from it.

State parameter

A random state value is generated per authorization attempt and checked against the value GitLab returns on redirect, rejecting the flow if they don't match (CSRF protection).

What this plugin does not do

  • It does not store, encrypt, or manage any files. Credential persistence, encryption at rest, and refresh scheduling are entirely OpenCode core's responsibility.
  • It does not validate Personal Access Tokens before they're saved (see Personal Access Token above).
  • It does not open a browser itself — OpenCode's client is responsible for presenting/opening the returned authorization URL.

Reporting security issues

Please report security vulnerabilities to the maintainers privately. Do not open public issues for security concerns.

Troubleshooting

OAuth: invalid_grant on token exchange

The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.

GitLab returns this one generic error for several distinct causes. Check them in this order:

  1. A stale authorization code. This is by far the most common cause. Codes are single-use and are bound to the PKCE verifier of the login attempt that created them, so all of these fail:

    • reloading the 127.0.0.1:8080/callback success page
    • opening an authorization URL from an earlier, abandoned attempt
    • starting a second login while a first one is still pending

    Close stray browser tabs pointing at /oauth/authorize or /callback, then start a completely fresh login.

  2. A custom GITLAB_OAUTH_CLIENT_ID whose application is misconfigured. Check which value the server has:

    opencode service get env GITLAB_OAUTH_CLIENT_ID

    That application must register http://127.0.0.1:8080/callback exactly (not localhost, no trailing slash, no different port), grant the api scope, and have Confidential unchecked — PKCE requires a public client and this plugin never sends a client_secret. Unset the variable to fall back to the default application, then opencode service restart.

The plugin appends the redirect_uri it sent and the client ID actually in effect to this error message — use that to confirm which cause applies.

OAuth: "The redirect URI included is not valid"

Shown by GitLab in the browser at the authorization step, before any code is issued. The application behind the client ID in use does not register http://127.0.0.1:8080/callback. If you set GITLAB_OAUTH_CLIENT_ID, add that exact redirect URI to your application, or unset the variable to use the default application.

OAuth: callback timeout / port 8080 unavailable

The callback server binds a fixed port (8080) to match the redirect URI registered on GitLab. If authentication never completes:

lsof -i :8080          # macOS/Linux
netstat -ano | findstr :8080   # Windows

Free the port or stop whatever else is using it, then retry.

PAT authentication fails on first model call

Since PATs are no longer validated when entered (see above), a bad token surfaces as a provider error on first use. Confirm the token is valid and scoped to api:

curl -H "Authorization: Bearer glpat-xxx" https://gitlab.com/api/v4/user

Self-managed GitLab issues

  • Enter the instance URL without a trailing slash or path, e.g. https://gitlab.example.com.
  • Verify TLS/connectivity independently:
    curl -v https://gitlab.example.com/api/v4/version

General

  • opencode plugin list should show gitlab-auth as active.
  • Consult OpenCode's own logs/diagnostics for plugin load errors; this plugin no longer writes its own log file.

Development

Setup

git clone https://gitlab.com/vglafirov/opencode-gitlab-auth.git
cd opencode-gitlab-auth
npm install
npm run prepare   # install Git hooks
npm run build

Available scripts

| Script | Description | | ---------------------- | -------------------------------- | | npm run build | Compile TypeScript to JavaScript | | npm run clean | Remove dist/ | | npm run rebuild | Clean and build | | npm test | Run unit tests (Vitest) | | npm run lint | Check code style (ESLint) | | npm run lint:fix | Fix code style issues | | npm run format | Format code with Prettier | | npm run format:check | Check code formatting | | npm run prepare | Install Git hooks (Husky) |

Manual verification

Automated tests only cover the pure instance-url.ts helpers. To exercise the plugin end to end:

  1. npm run build
  2. Point a project's opencode.jsonc plugins array at the local build directory (see Local development install).
  3. Confirm opencode plugin list shows gitlab-auth.
  4. Run the OAuth flow against gitlab.com, then against a self-managed instance.
  5. Force a credential refresh (wait for expiry, or shorten it locally) and confirm OpenCode calls back into refresh successfully.
  6. Run the PAT flow with a self-managed instance URL and confirm the resulting provider call targets that instance.
  7. Remove/reload the plugin and confirm no listener is left on port 8080.

Commit messages

Follows Conventional Commits, enforced by commitlint via a Husky pre-commit hook:

feat(oauth): add support for custom redirect URIs
fix(server): resolve callback timeout issue
feat!: migrate to opencode plugin SDK v2

BREAKING CHANGE: requires OpenCode v2 and @opencode/plugin ^2.0.0

Release process

Releases are automated with semantic-release from the main branch: feat: → minor, fix:/perf:/revert:/ docs: → patch, feat!:/BREAKING CHANGE: → major.

Upgrading from 2.x

Version 3.0 targets OpenCode's plugin SDK v2 (@opencode/plugin) and is not compatible with OpenCode v1 or the @opencode-ai/plugin package that 2.x depended on. If you're on OpenCode v1, stay on opencode-gitlab-auth@^2.

What changed for users:

  • Config key: "plugin": [...] → "plugins": [...] in opencode.json(c).
  • Existing v1 credentials in ~/.local/share/opencode/auth.json are not migrated. OpenCode v2 stores credentials in its own credential store; re-authenticate once after upgrading.
  • The debug log at ~/.local/share/opencode/log/gitlab-auth.log no longer exists.
  • The plugin no longer opens your browser itself; OpenCode's client is responsible for that.
  • PATs are no longer validated against the GitLab API before being saved (see Personal Access Token).
  • The gitlab provider itself (models, gitlab-ai-provider wiring) now ships with OpenCode core — this plugin only adds the OAuth method and the instance-URL form field.

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines on code style, the development workflow, and submitting merge requests.

Quick checklist before opening an MR:

npm run lint
npm run format:check
npm run build
npm test

FAQ

Do I need to register my own OAuth app?

Yes. The bundled client ID's redirect URI does not match OpenCode's local callback server, so OAuth will not work without setting GITLAB_OAUTH_CLIENT_ID to your own application. See Quick Start.

Can I use this with self-managed GitLab?

Yes — enter your instance URL when prompted, for either OAuth or PAT.

Where are my credentials stored?

In OpenCode core's own credential store, not in a file this plugin manages. See Credential storage.

Can I use this in CI/CD?

Yes, via the Personal Access Token method — OAuth requires a browser.

How long do OAuth tokens last, and how are they refreshed?

GitLab OAuth access tokens are short-lived; OpenCode core calls this plugin's refresh handler automatically before they expire, exchanging the stored refresh token for a new access token.

Changelog

See CHANGELOG.md for version history and release notes.

License

MIT License — see LICENSE file for details.

Copyright (c) 2025 OpenCode GitLab Auth Contributors

Acknowledgments

Links


Made with ❤️ for the OpenCode community