{"_id":"@bestsolution/verdaccio-gitlab-oidc-auth","_rev":"3-771a55a622e88686a022a3a0c30d8385","name":"@bestsolution/verdaccio-gitlab-oidc-auth","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@bestsolution/verdaccio-gitlab-oidc-auth","version":"1.0.0","keywords":["verdaccio","verdaccio-plugin","verdaccio-auth","gitlab","oidc","ci","authentication"],"author":{"name":"udo.rader@bestsolution.at"},"license":"GPL-3.0-only","_id":"@bestsolution/verdaccio-gitlab-oidc-auth@1.0.0","maintainers":[{"name":"tomsontom","email":"tom.schindl@bestsolution.at"},{"name":"udotirol","email":"udo.rader@bestsolution.at"}],"homepage":"https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth","bugs":{"url":"https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth/issues"},"dist":{"shasum":"47bd510d7f9b98566da87ee295a224305142af87","tarball":"https://registry.npmjs.org/@bestsolution/verdaccio-gitlab-oidc-auth/-/verdaccio-gitlab-oidc-auth-1.0.0.tgz","fileCount":9,"integrity":"sha512-imknzchtvvyuCacFb2T0SWKgXZYHm1ya4oxppfgq4+VKqsZWTOHbPMtyf0a+KJr5xEgXsVGJ8aOLeoSUOlOZFQ==","signatures":[{"sig":"MEUCIDJBkA41nGNHCpKD3E6wzaFoKqBAczejcrR+vW3B4BclAiEAw575k5vYjzWcRbxhQDKvIwDOU2lo7brBRne2sSbO58U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":51405},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"gitHead":"6fd13cc7fc741ba8b7f552248c6f0a80b5ba5fdf","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"udotirol","email":"udo.rader@bestsolution.at"},"repository":{"url":"git+https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth.git","type":"git"},"_npmVersion":"11.7.0","description":"Verdaccio auth plugin for GitLab CI OIDC ID tokens","directories":{},"_nodeVersion":"25.4.0","dependencies":{"jose":"^6.1.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.17","typescript":"^5.9.3","@verdaccio/core":"^8.0.0-next-8.28","@verdaccio/types":"^13.0.0-next-8.10","@verdaccio/config":"^8.0.0-next-8.28","@types/jsonwebtoken":"^9.0.10"},"peerDependencies":{"verdaccio":">=6"},"_npmOperationalInternal":{"tmp":"tmp/verdaccio-gitlab-oidc-auth_1.0.0_1770909924991_0.013200941990868298","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@bestsolution/verdaccio-gitlab-oidc-auth","version":"1.0.1","keywords":["verdaccio","verdaccio-plugin","verdaccio-auth","gitlab","oidc","ci","authentication"],"author":{"name":"udo.rader@bestsolution.at"},"license":"GPL-3.0-only","_id":"@bestsolution/verdaccio-gitlab-oidc-auth@1.0.1","maintainers":[{"name":"tomsontom","email":"tom.schindl@bestsolution.at"},{"name":"udotirol","email":"udo.rader@bestsolution.at"}],"homepage":"https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth","bugs":{"url":"https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth/issues"},"dist":{"shasum":"337bfeedd2f31bce66a720e94493d41bd82198e4","tarball":"https://registry.npmjs.org/@bestsolution/verdaccio-gitlab-oidc-auth/-/verdaccio-gitlab-oidc-auth-1.0.1.tgz","fileCount":9,"integrity":"sha512-fanMMpwPgd3zTrLKqdauuBXFYBPLHlYsqdclKzHf4uJ6qiPcOsbMunaZ6JuXMyH0ajsx4uCM6ukz2NA5W5+LdA==","signatures":[{"sig":"MEYCIQCbcBzu6BAuYDVAzxztIT5BH2uMPQ4u5PvIbgw2+1/tbAIhALYsnWZ9cfz32HlUS7HH4dyjL6bpVzXSEJ3FGmY/tq3c","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":51109},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"gitHead":"72a5a99aec8f5cf075dfd4f118aae3a818186ff5","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"udotirol","email":"udo.rader@bestsolution.at"},"repository":{"url":"git+https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth.git","type":"git"},"_npmVersion":"11.7.0","description":"Verdaccio auth plugin for GitLab CI OIDC ID tokens","directories":{},"_nodeVersion":"25.4.0","dependencies":{"jose":"^6.1.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.17","typescript":"^5.9.3","@verdaccio/core":"^8.0.0-next-8.28","@verdaccio/types":"^13.0.0-next-8.10","@verdaccio/config":"^8.0.0-next-8.28","@types/jsonwebtoken":"^9.0.10"},"peerDependencies":{"verdaccio":">=6"},"_npmOperationalInternal":{"tmp":"tmp/verdaccio-gitlab-oidc-auth_1.0.1_1770971654120_0.08515670872985592","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@bestsolution/verdaccio-gitlab-oidc-auth","version":"1.0.2","description":"Verdaccio auth plugin for GitLab CI OIDC ID tokens","keywords":["verdaccio","verdaccio-plugin","verdaccio-auth","gitlab","oidc","ci","authentication"],"homepage":"https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth","repository":{"type":"git","url":"git+https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth.git"},"license":"GPL-3.0-only","author":{"name":"udo.rader@bestsolution.at"},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"prepublishOnly":"npm run build","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","test":"vitest run","test:watch":"vitest","type-check":"tsc --noEmit"},"dependencies":{"jose":"^6.1.3"},"devDependencies":{"@types/jsonwebtoken":"^9.0.10","@verdaccio/config":"^8.0.0-next-8.28","@verdaccio/core":"^8.0.0-next-8.28","@verdaccio/types":"^13.0.0-next-8.10","typescript":"^5.9.3","vitest":"^4.0.17"},"peerDependencies":{"verdaccio":">=6"},"engines":{"node":">=18"},"publishConfig":{"access":"public"},"gitHead":"888121bd3122052abd4c33f13cace20179cfac04","_id":"@bestsolution/verdaccio-gitlab-oidc-auth@1.0.2","bugs":{"url":"https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth/issues"},"_nodeVersion":"25.4.0","_npmVersion":"11.7.0","dist":{"integrity":"sha512-LG2tzsots1saWTcSyLqQ/FCRZVqUdTqls8VzvK4vg3M5NXZjk7w33O+hU9UQkypOWGtq2Bzudb8buWUp86tb8A==","shasum":"3ad3924e5b67325b9f79cf6254b080ec57f29e39","tarball":"https://registry.npmjs.org/@bestsolution/verdaccio-gitlab-oidc-auth/-/verdaccio-gitlab-oidc-auth-1.0.2.tgz","fileCount":9,"unpackedSize":51725,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCwlte248wruQRJKUSrARQTaYuPy1myituem8XDnwCHSgIhAOZPtEcNjUaRwqEZ+nGLxMV/OIkhKu2Pj6FmKBk5aUH5"}]},"_npmUser":{"name":"udotirol","email":"udo.rader@bestsolution.at"},"directories":{},"maintainers":[{"name":"tomsontom","email":"tom.schindl@bestsolution.at"},{"name":"udotirol","email":"udo.rader@bestsolution.at"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/verdaccio-gitlab-oidc-auth_1.0.2_1770999236650_0.08795150621642045"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-12T15:25:24.914Z","modified":"2026-02-13T16:13:56.942Z","1.0.0":"2026-02-12T15:25:25.151Z","1.0.1":"2026-02-13T08:34:14.261Z","1.0.2":"2026-02-13T16:13:56.814Z"},"bugs":{"url":"https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth/issues"},"author":{"name":"udo.rader@bestsolution.at"},"license":"GPL-3.0-only","homepage":"https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth","keywords":["verdaccio","verdaccio-plugin","verdaccio-auth","gitlab","oidc","ci","authentication"],"repository":{"type":"git","url":"git+https://github.com/BestSolution-at/verdaccio-gitlab-oidc-auth.git"},"description":"Verdaccio auth plugin for GitLab CI OIDC ID tokens","maintainers":[{"name":"tomsontom","email":"tom.schindl@bestsolution.at"},{"name":"udotirol","email":"udo.rader@bestsolution.at"}],"readme":"# @bestsolution/verdaccio-gitlab-oidc-auth\n\nA [Verdaccio](https://verdaccio.org/) auth plugin that authenticates GitLab CI\njobs using [OIDC ID tokens](https://docs.gitlab.com/ci/secrets/id_token_authentication/).\n\nGitLab CI pipelines present a short-lived JWT as the password via HTTP Basic\nAuth. The plugin verifies the token cryptographically against the GitLab JWKS\nendpoint and derives Verdaccio groups from the JWT claims. This allows\nfine-grained package access control without long-lived credentials.\n\n## How It Works\n\n1. GitLab CI job requests an OIDC ID token (`id_tokens` keyword, GitLab 15.7+)\n2. The job authenticates to Verdaccio using HTTP Basic Auth:\n   - **username**: `gitlab-oidc` (configurable)\n   - **password**: the OIDC JWT\n3. The plugin verifies the JWT signature via the GitLab JWKS endpoint\n4. On success, the plugin returns Verdaccio groups derived from the JWT claims\n5. Non-CI users (any other username) pass through to the next auth plugin\n   (typically htpasswd)\n\n## Groups\n\nEvery valid JWT receives the base group `gitlab-ci`. Additional groups depend\non branch/tag protection status and the `project_groups` configuration option.\n\n### Base Groups (always assigned)\n\n| Condition | Group |\n|-----------|-------|\n| Every valid JWT | `gitlab-ci` |\n| JWT with `ref_protected: \"true\"` | `gitlab-ci-protected` |\n\n### Project Groups (when `project_groups: true`)\n\nWhen `project_groups` is enabled, the plugin derives additional groups from\nthe `namespace_path` and `project_path` JWT claims. These groups include\ncomposite variants that combine project identity with branch/tag protection\nstatus.\n\n| Condition | Group |\n|-----------|-------|\n| Every valid JWT | `gitlab-ci:<namespace_path>` |\n| Every valid JWT | `gitlab-ci:<project_path>` |\n| JWT with `ref_protected: \"true\"` | `gitlab-ci-protected:<namespace_path>` |\n| JWT with `ref_protected: \"true\"` | `gitlab-ci-protected:<project_path>` |\n\n### Examples\n\n**Protected branch/tag push** from project `my-group/my-project` on `main`\n(with `project_groups: true`):\n\n```text\ngitlab-ci\ngitlab-ci-protected\ngitlab-ci:my-group\ngitlab-ci-protected:my-group\ngitlab-ci:my-group/my-project\ngitlab-ci-protected:my-group/my-project\n```\n\n**Feature branch/tag push** from the same project on `feature/foo`\n(with `project_groups: true`):\n\n```text\ngitlab-ci\ngitlab-ci:my-group\ngitlab-ci:my-group/my-project\n```\n\nNote: feature branches/tags are not protected, so no `gitlab-ci-protected` groups\nare assigned. This distinction is critical for controlling who can publish\npackages (see [Authorization](#authorization) below).\n\n**Nested subgroup project** `my-org/team-a/libs/core` on protected branch `main`\n(with `project_groups: true`):\n\n```text\ngitlab-ci\ngitlab-ci-protected\ngitlab-ci:my-org/team-a/libs\ngitlab-ci-protected:my-org/team-a/libs\ngitlab-ci:my-org/team-a/libs/core\ngitlab-ci-protected:my-org/team-a/libs/core\n```\n\n## Installation\n\n```bash\nnpm install @bestsolution/verdaccio-gitlab-oidc-auth\n```\n\n## Configuration\n\nIn your Verdaccio `config.yaml`:\n\n```yaml\nauth:\n  \"@bestsolution/verdaccio-gitlab-oidc-auth\":\n    gitlab_url: https://gitlab.example.com\n    audience: https://npm.example.com\n    # ci_username: gitlab-oidc          # optional, default: \"gitlab-oidc\"\n    # jwks_cache_ttl: 86400             # optional, default: 86400 (seconds)\n    # project_groups: false             # optional, default: false\n  htpasswd:\n    file: ./htpasswd\n```\n\nThe plugin **must** appear before `htpasswd` in the `auth:` section so that\nCI tokens are verified first. Non-CI usernames fall through to htpasswd.\n\n### Options\n\n| Option | Required | Default | Description |\n|--------|----------|---------|-------------|\n| `gitlab_url` | yes | -- | GitLab instance URL (e.g. `https://gitlab.example.com`) |\n| `audience` | yes | -- | Expected `aud` claim in the JWT (must match `aud` in GitLab `id_tokens`) |\n| `ci_username` | no | `gitlab-oidc` | Username that triggers OIDC authentication |\n| `jwks_cache_ttl` | no | `86400` | How long to cache the JWKS keys (seconds) |\n| `project_groups` | no | `false` | Derive project-level and namespace-level groups from JWT claims (see [Project Groups](#project-groups-when-project_groups-true)) |\n\n## Authorization\n\nThe plugin itself does **not** implement authorization. It returns groups, and\nVerdaccio's built-in `packages:` configuration controls which groups can\naccess or publish to which scopes.\n\n### Understanding Verdaccio's Group Matching\n\nVerdaccio's `publish:` (and `access:`) fields accept a space-separated list of\ngroups. A user is authorized if they belong to **any** of the listed groups\n(OR logic). There is no AND logic.\n\nFor example:\n\n```yaml\npublish: gitlab-ci-protected tom\n```\n\nThis means: allow publishing if the user has the `gitlab-ci-protected` group\n**OR** is the user `tom`. This is important to understand when designing your\naccess control rules.\n\n### Package Access Examples\n\n#### Simple: Any Protected CI Build Can Publish\n\n```yaml\npackages:\n  \"@my-scope/*\":\n    access: $authenticated\n    publish: gitlab-ci-protected deploy-admin\n```\n\nAny CI job running on a protected branch or protected tag (from any GitLab project)\ncan publish to `@my-scope/*`. This is simple but does not isolate projects from\neach other.\n\n#### Project-Level Isolation\n\nWith `project_groups: true`, you can restrict publishing to specific projects:\n\n```yaml\npackages:\n  \"@my-scope/*\":\n    access: $authenticated\n    publish: gitlab-ci-protected:my-group/my-project deploy-admin\n```\n\nOnly protected-branch/tag CI jobs from `my-group/my-project` can publish to\n`@my-scope/*`. A protected-branch/tag build from `other-group/other-project`\ncannot publish here, even though it also has `gitlab-ci-protected`.\n\n#### Namespace-Level Isolation\n\nRestrict publishing to any project within a GitLab group:\n\n```yaml\npackages:\n  \"@my-scope/*\":\n    access: $authenticated\n    publish: gitlab-ci-protected:my-group deploy-admin\n```\n\nAny project under the `my-group` namespace (on a protected branch/tag) can publish.\n\n#### Multiple Scopes With Different Policies\n\n```yaml\npackages:\n  \"@internal/*\":\n    access: $authenticated\n    publish: gitlab-ci-protected:my-org/team-a/libs deploy-admin\n\n  \"@shared/*\":\n    access: $authenticated\n    publish: gitlab-ci-protected:my-org deploy-admin\n\n  \"**\":\n    access: $all\n    publish: deploy-admin\n    proxy: npmjs\n```\n\n### Security Considerations\n\n- **Branch/tag protection matters**: Only branches and tags marked as \"protected\"\n  in GitLab produce the `gitlab-ci-protected` groups. Without `project_groups`,\n  any project with a protected branch/tag can publish to scopes that require\n  `gitlab-ci-protected`. Enable `project_groups: true` and use composite groups\n  (e.g. `gitlab-ci-protected:my-group/my-project`) to restrict publishing to\n  specific projects.\n\n- **No long-lived credentials**: OIDC tokens are short-lived JWTs (typically\n  5 minutes). They cannot be reused after expiry and do not need to be rotated\n  or revoked manually.\n\n- **Cryptographic verification only**: The plugin verifies JWT signatures\n  against the GitLab JWKS endpoint. It does not make API calls to GitLab and\n  does not require network access beyond the JWKS endpoint.\n\n- **JWKS caching**: Public keys are cached in memory. The cache is refreshed\n  when an unknown `kid` is encountered (key rotation) or after the configured\n  TTL expires.\n\n## GitLab CI Usage\n\nThe plugin only supports (and requires) Basic Auth:\n\n```yaml\npublish:\n  image: node:22\n  id_tokens:\n    VERDACCIO_TOKEN:\n      aud: https://npm.example.com\n  script:\n    - AUTH=$(echo -n \"gitlab-oidc:${VERDACCIO_TOKEN}\" | base64 -w0)\n    - echo \"//${VERDACCIO_HOST}/:_auth=${AUTH}\" > .npmrc\n    - npm publish\n```\n\n> **Note**: The `id_tokens` keyword requires GitLab 15.7 or later.\n\n## Development\n\n```bash\nnpm install\nnpm run build\nnpm test\n```\n\n## License\n\nGPL-3.0-only\n","readmeFilename":"README.md"}