All posts
launchpreview-environmentstechnical

GitLab Previews Are Live — and the OAuth Grant Is Read-Only by Default

PreviewDrop Team·September 6, 2026·5 min read

Every deploy tool we looked at asks GitLab for the same thing on its consent screen: api — "Grants complete read/write access to the API, including all groups and projects." That is the whole account. For a tool whose job is to clone a branch and show you a URL, it is far more than the job needs, and it is the line that makes people close the tab.

Starting today, PreviewDrop supports GitLab.com, and its consent screen asks for two read-only scopes. Nothing else, unless you turn it on yourself.

What shipped

  • Connect GitLab from Settings → Integrations. One connection per workspace; every member imports through it.
  • Import any project you can see from the GitLab tab of Connect repo, nested groups included (group/subgroup/project works).
  • Push a branch and it builds the way a GitHub repo does: framework detection or your Dockerfile, a real container, a real HTTPS URL, the same auto-expiry.
  • Available on every plan, including Free.

Why the default is read-only

GitLab's OAuth scopes are coarse. read_api lets an app list your projects and branches. read_repository lets it clone over HTTPS and read files. Those two are everything a preview needs.

The moment an app wants to write anything — register a webhook, post a note on a merge request, set a commit status — the only scope GitLab offers is api, which is read and write on all of it. There is no "webhooks only" or "comments only" scope, the way GitHub Apps have fine-grained permissions.

We could have asked for api up front, like everyone else. We chose not to. The default connection requests exactly this:

scope=read_api read_repository

and the consent screen shows exactly two lines, both labelled read-only.

The two levels

Read-only (default)Automation (opt-in)
Scopes requestedread_api read_repositoryapi read_repository
List projects and branchesyesyes
Clone and build previewsyesyes
Webhook on imported projectsyou add it, one paste per projectcreated for you
Preview URL as an MR commentnoyes
Commit status on the pipelinenoyes

Read-only is a complete product. When you import a project, PreviewDrop mints a webhook secret for it and shows you the payload URL, the secret and the two triggers to tick. Paste them into the project's webhook settings once, and every push on every branch builds a preview from then on. Merge requests build too; they just are not announced with a comment, because commenting needs write access we do not have.

Automation is one button on the Integrations card, Enable automation. It sends you back through GitLab's consent screen, this time asking for api, and from then on PreviewDrop registers the webhook on each project you import and posts the live URL on every merge request, updating the same note on each push instead of adding a new one.

The scope only ever widens when you click that button. A plain reconnect stays at the level you already granted.

One secret per project

GitLab authenticates webhook deliveries with a shared token in the X-Gitlab-Token header. Rather than one token for the whole platform, each imported project gets its own. A delivery is checked against that project's secret and routed to that project only, so a secret that leaks from one repository cannot be used to trigger builds in another workspace.

Tokens themselves are stored encrypted per workspace, refreshed automatically before they expire, and dropped the moment you disconnect the provider. Repositories are cloned into the build, then discarded; we never keep your source.

What the setup looks like

  1. Settings → Integrations → Connect GitLab. Authorise the two read-only scopes.
  2. Connect repo → GitLab tab. Pick a project and a branch. The first build starts immediately if you leave Deploy on import on.
  3. Project → Settings → GitLab webhook. Copy the URL and secret into the project's webhook settings on GitLab, ticking Push events and Merge request events. Or skip this step by enabling automation first.

The webhook card tells you which state you are in: installed, waiting for you, or blocked on a permission you do not have on that repository (creating hooks needs Maintainer). The full breakdown is in the Git integration docs.

What this does not do yet

  • Self-hosted GitLab is available on Enterprise by arrangement, not self-serve.
  • Bitbucket Cloud is next. Its OAuth model is different again — permissions live on the consumer, not the request — so the same read-only-first design needs two registered consumers. The plumbing is in place; the connection opens as soon as they are.
  • Read-only connections do not comment on merge requests. That is the trade, and it is yours to make.

If your code lives on GitLab and you have been running previews through a CI job with a long-lived token, connect it read-only and see what a preview looks like when the tool cannot write to your account.

Ready to give every branch a live URL?

Free tier — 2 concurrent previews, no credit card required.

Start free