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

@bestsolution/verdaccio-gitlab-oidc-auth

v1.0.2

Published

Verdaccio auth plugin for GitLab CI OIDC ID tokens

Readme

@bestsolution/verdaccio-gitlab-oidc-auth

A Verdaccio auth plugin that authenticates GitLab CI jobs using OIDC ID tokens.

GitLab CI pipelines present a short-lived JWT as the password via HTTP Basic Auth. The plugin verifies the token cryptographically against the GitLab JWKS endpoint and derives Verdaccio groups from the JWT claims. This allows fine-grained package access control without long-lived credentials.

How It Works

  1. GitLab CI job requests an OIDC ID token (id_tokens keyword, GitLab 15.7+)
  2. The job authenticates to Verdaccio using HTTP Basic Auth:
    • username: gitlab-oidc (configurable)
    • password: the OIDC JWT
  3. The plugin verifies the JWT signature via the GitLab JWKS endpoint
  4. On success, the plugin returns Verdaccio groups derived from the JWT claims
  5. Non-CI users (any other username) pass through to the next auth plugin (typically htpasswd)

Groups

Every valid JWT receives the base group gitlab-ci. Additional groups depend on branch/tag protection status and the project_groups configuration option.

Base Groups (always assigned)

| Condition | Group | |-----------|-------| | Every valid JWT | gitlab-ci | | JWT with ref_protected: "true" | gitlab-ci-protected |

Project Groups (when project_groups: true)

When project_groups is enabled, the plugin derives additional groups from the namespace_path and project_path JWT claims. These groups include composite variants that combine project identity with branch/tag protection status.

| Condition | Group | |-----------|-------| | Every valid JWT | gitlab-ci:<namespace_path> | | Every valid JWT | gitlab-ci:<project_path> | | JWT with ref_protected: "true" | gitlab-ci-protected:<namespace_path> | | JWT with ref_protected: "true" | gitlab-ci-protected:<project_path> |

Examples

Protected branch/tag push from project my-group/my-project on main (with project_groups: true):

gitlab-ci
gitlab-ci-protected
gitlab-ci:my-group
gitlab-ci-protected:my-group
gitlab-ci:my-group/my-project
gitlab-ci-protected:my-group/my-project

Feature branch/tag push from the same project on feature/foo (with project_groups: true):

gitlab-ci
gitlab-ci:my-group
gitlab-ci:my-group/my-project

Note: feature branches/tags are not protected, so no gitlab-ci-protected groups are assigned. This distinction is critical for controlling who can publish packages (see Authorization below).

Nested subgroup project my-org/team-a/libs/core on protected branch main (with project_groups: true):

gitlab-ci
gitlab-ci-protected
gitlab-ci:my-org/team-a/libs
gitlab-ci-protected:my-org/team-a/libs
gitlab-ci:my-org/team-a/libs/core
gitlab-ci-protected:my-org/team-a/libs/core

Installation

npm install @bestsolution/verdaccio-gitlab-oidc-auth

Configuration

In your Verdaccio config.yaml:

auth:
  "@bestsolution/verdaccio-gitlab-oidc-auth":
    gitlab_url: https://gitlab.example.com
    audience: https://npm.example.com
    # ci_username: gitlab-oidc          # optional, default: "gitlab-oidc"
    # jwks_cache_ttl: 86400             # optional, default: 86400 (seconds)
    # project_groups: false             # optional, default: false
  htpasswd:
    file: ./htpasswd

The plugin must appear before htpasswd in the auth: section so that CI tokens are verified first. Non-CI usernames fall through to htpasswd.

Options

| Option | Required | Default | Description | |--------|----------|---------|-------------| | gitlab_url | yes | -- | GitLab instance URL (e.g. https://gitlab.example.com) | | audience | yes | -- | Expected aud claim in the JWT (must match aud in GitLab id_tokens) | | ci_username | no | gitlab-oidc | Username that triggers OIDC authentication | | jwks_cache_ttl | no | 86400 | How long to cache the JWKS keys (seconds) | | project_groups | no | false | Derive project-level and namespace-level groups from JWT claims (see Project Groups) |

Authorization

The plugin itself does not implement authorization. It returns groups, and Verdaccio's built-in packages: configuration controls which groups can access or publish to which scopes.

Understanding Verdaccio's Group Matching

Verdaccio's publish: (and access:) fields accept a space-separated list of groups. A user is authorized if they belong to any of the listed groups (OR logic). There is no AND logic.

For example:

publish: gitlab-ci-protected tom

This means: allow publishing if the user has the gitlab-ci-protected group OR is the user tom. This is important to understand when designing your access control rules.

Package Access Examples

Simple: Any Protected CI Build Can Publish

packages:
  "@my-scope/*":
    access: $authenticated
    publish: gitlab-ci-protected deploy-admin

Any CI job running on a protected branch or protected tag (from any GitLab project) can publish to @my-scope/*. This is simple but does not isolate projects from each other.

Project-Level Isolation

With project_groups: true, you can restrict publishing to specific projects:

packages:
  "@my-scope/*":
    access: $authenticated
    publish: gitlab-ci-protected:my-group/my-project deploy-admin

Only protected-branch/tag CI jobs from my-group/my-project can publish to @my-scope/*. A protected-branch/tag build from other-group/other-project cannot publish here, even though it also has gitlab-ci-protected.

Namespace-Level Isolation

Restrict publishing to any project within a GitLab group:

packages:
  "@my-scope/*":
    access: $authenticated
    publish: gitlab-ci-protected:my-group deploy-admin

Any project under the my-group namespace (on a protected branch/tag) can publish.

Multiple Scopes With Different Policies

packages:
  "@internal/*":
    access: $authenticated
    publish: gitlab-ci-protected:my-org/team-a/libs deploy-admin

  "@shared/*":
    access: $authenticated
    publish: gitlab-ci-protected:my-org deploy-admin

  "**":
    access: $all
    publish: deploy-admin
    proxy: npmjs

Security Considerations

  • Branch/tag protection matters: Only branches and tags marked as "protected" in GitLab produce the gitlab-ci-protected groups. Without project_groups, any project with a protected branch/tag can publish to scopes that require gitlab-ci-protected. Enable project_groups: true and use composite groups (e.g. gitlab-ci-protected:my-group/my-project) to restrict publishing to specific projects.

  • No long-lived credentials: OIDC tokens are short-lived JWTs (typically 5 minutes). They cannot be reused after expiry and do not need to be rotated or revoked manually.

  • Cryptographic verification only: The plugin verifies JWT signatures against the GitLab JWKS endpoint. It does not make API calls to GitLab and does not require network access beyond the JWKS endpoint.

  • JWKS caching: Public keys are cached in memory. The cache is refreshed when an unknown kid is encountered (key rotation) or after the configured TTL expires.

GitLab CI Usage

The plugin only supports (and requires) Basic Auth:

publish:
  image: node:22
  id_tokens:
    VERDACCIO_TOKEN:
      aud: https://npm.example.com
  script:
    - AUTH=$(echo -n "gitlab-oidc:${VERDACCIO_TOKEN}" | base64 -w0)
    - echo "//${VERDACCIO_HOST}/:_auth=${AUTH}" > .npmrc
    - npm publish

Note: The id_tokens keyword requires GitLab 15.7 or later.

Development

npm install
npm run build
npm test

License

GPL-3.0-only