Repository Tokens: Create One and Authenticate

A repository token is the credential your build tools send to CloudRepo. Other tools call the same thing an access token or an API token. You create it in the admin portal, choose which repositories it reaches and whether it can publish, and paste it into Maven, Gradle, npm, pip, twine or Docker in place of a password.

Every repository token:

  • reaches only the repositories you tick when you create it;

  • is Read only (download) or Read + write (download, publish and delete);

  • expires: the default is 90 days and the maximum is 90 days;

  • is shown once, when you create it, and starts with crp_v1_;

  • can be revoked on its own, without changing your password or your other tokens.

This page uses four placeholders. [org-id] is your organization’s name, the first part of your repository host: for acme.mycloudrepo.io it is acme. [repo-name] is the repository’s name as it appears in the admin portal. you@example.com stands for your email address, and YOUR_REPOSITORY_TOKEN for the token. Never commit a real token to version control.

The Credential Every Client Sends

Client

Username

Password or token

Maven, Gradle, sbt, Leiningen, deps.edn, pip, twine, Poetry, uv, curl

Your email address, in lower case: the email of the account that created the token

The repository token

npm (and pnpm, which reads .npmrc)

None

The repository token, as _authToken in .npmrc

Docker (docker login)

Your email address (Docker does not check it)

The repository token

The username is your email address, not the token’s name and not a placeholder. Type it in lower case. For the clients that send a username (Maven, Gradle, sbt, Leiningen, deps.edn, pip, twine, Poetry, uv, curl and npm _auth), a username of __token__, token, an email with a capital letter, or anything other than the email of the account that created the token is refused with 401 Unauthorized. (__token__ is the convention on pypi.org; CloudRepo does not use it.) Docker does not check the username; use your email address there anyway.

Important

Create the token while signed in as a user of your organization, not as the organization owner. Maven, Gradle, pip, twine and the other clients that send a username do not yet reliably accept a token created by the owner account. npm (_authToken) and Docker do. If your builds have no user yet, add one under Users (see Create Your First User), sign in to the admin portal as that user, and create the token there. That user’s email address is then the username.

Create a Token in the Admin Portal

  1. Sign in to the CloudRepo Admin Portal as the user whose builds will use the token, and in the sidebar, click Repository Tokens.

    Expected: the Repository Tokens page. If you have no tokens yet, it shows No repository tokens yet.

  2. Click Create token, or Create your first token if you have none.

    Expected: the Create repository token form, with Name, Scope, Repositories and Expires. If it shows No repositories available instead, your organization has no repositories yet: create one first (see Create Your First Repository).

  3. In Name, type a name that says where the token will be used, such as ci-maven or laptop-npm.

    Expected: the field holds the name. The name is a label for the token list; it is never the username.

  4. Under Scope, choose Read only to download, or Read + write to download, publish and delete.

    Expected: one option is selected.

  5. Under Repositories, tick each repository the token may reach.

    Expected: the repositories you need are ticked. This choice is permanent: to reach different repositories later, create a new token.

  6. Under Expires, keep the default or pick an earlier date. The token stops working at the start of that date (00:00 UTC), so pick a date after the last day you need it.

    Expected: the form reads Default 90 days. Maximum 90 days from today. When the token expires, builds that use it stop authenticating, and you create a new one.

  7. Click Create token.

    Expected: a green Token created banner and the token value. If you have not confirmed your identity in the last 15 minutes, a Confirm Your Identity dialog comes first: enter your password and click Confirm. If the form says Your account is read-only and cannot create a write-enabled token., your user has read only access: choose Read only, or ask your organization owner for write access.

  8. Click Copy next to the token, and store it where your build tool will read it, such as a CI secret.

    Expected: the button reads Copied!. CloudRepo shows the token only this once. If you lose it, rotate the token from the Repository Tokens page to get a new value.

Maven (settings.xml)

Put the credential in ~/.m2/settings.xml. The <id> is any name you choose; it must match the <id> of the repository in your pom.xml.

~/.m2/settings.xml
<settings>
  <servers>
    <server>
      <id>cloudrepo</id>
      <username>you@example.com</username>
      <password>${env.CLOUDREPO_TOKEN}</password>
    </server>
  </servers>
</settings>

${env.CLOUDREPO_TOKEN} reads the token from an environment variable, which keeps it out of the file. You can paste the token there instead.

pom.xml
<repositories>
  <repository>
    <id>cloudrepo</id>
    <url>https://[org-id].mycloudrepo.io/repositories/[repo-name]</url>
  </repository>
</repositories>

<distributionManagement>
  <repository>
    <id>cloudrepo</id>
    <url>https://[org-id].mycloudrepo.io/repositories/[repo-name]</url>
  </repository>
</distributionManagement>

More: Maven Repositories.

Gradle

Keep the credential in ~/.gradle/gradle.properties, outside your project:

~/.gradle/gradle.properties
cloudrepoUsername=you@example.com
cloudrepoToken=YOUR_REPOSITORY_TOKEN

Then read it in the build script.

build.gradle.kts
repositories {
    maven {
        url = uri("https://[org-id].mycloudrepo.io/repositories/[repo-name]")
        credentials {
            username = providers.gradleProperty("cloudrepoUsername").get()
            password = providers.gradleProperty("cloudrepoToken").get()
        }
    }
}
build.gradle
repositories {
    maven {
        url = "https://[org-id].mycloudrepo.io/repositories/[repo-name]"
        credentials {
            username = findProperty("cloudrepoUsername")
            password = findProperty("cloudrepoToken")
        }
    }
}

To publish, use the same maven { ... } block under publishing { repositories { ... } }. More: Gradle Repositories.

npm (.npmrc)

npm sends the token on its own, with no username. Put it in the project’s .npmrc, or in ~/.npmrc:

.npmrc
registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/
//[org-id].mycloudrepo.io/repositories/[repo-name]/:_authToken=${CLOUDREPO_TOKEN}

npm replaces ${CLOUDREPO_TOKEN} with the environment variable of that name, so the file can be committed without the token in it. Keep the trailing / on both lines, and keep the //[org-id]... line identical to the registry URL without https:.

  • npm login also works, for a local npm repository: it opens the admin portal in your browser and writes a token to .npmrc for you. That token starts with crn_v1_ and works only on the local npm repository you ran npm login against. A proxy npm repository refuses it with 403 (npm_token_not_supported_on_proxy), even though npm login against the proxy succeeds; use a token from Repository Tokens there.

  • An npm login token is read-only unless you choose Read-write when you authorize it and your user has write access, so npm publish answers 403 with a read-only one. A token created under Repository Tokens works on every npm repository it reaches, local or proxy.

  • npm token create is not supported: create tokens in the admin portal.

More: npm Repositories and npm Client.

pip (pip.conf or --index-url)

pip takes the credential inside the index URL. Write the @ in your email address as %40:

pip.conf (pip.ini on Windows)
[global]
index-url = https://you%40example.com:YOUR_REPOSITORY_TOKEN@[org-id].mycloudrepo.io/repositories/[repo-name]/simple/

The same URL works for a single command:

pip install my-package \
  --index-url "https://you%40example.com:${CLOUDREPO_TOKEN}@[org-id].mycloudrepo.io/repositories/[repo-name]/simple/"

In CI, set PIP_INDEX_URL to that URL from a secret instead of writing a file. More, including keeping the token in your system keyring: Basic Configuration Examples.

twine (.pypirc)

twine uploads to the repository URL without /simple/. Its username is a plain field, so the @ needs no encoding:

~/.pypirc
[distutils]
index-servers = cloudrepo

[cloudrepo]
repository = https://[org-id].mycloudrepo.io/repositories/[repo-name]
username = you@example.com
password = YOUR_REPOSITORY_TOKEN
twine upload --repository cloudrepo dist/*

In CI, skip the file and set the environment instead:

export TWINE_USERNAME="you@example.com"
export TWINE_PASSWORD="$CLOUDREPO_TOKEN"
twine upload --repository-url https://[org-id].mycloudrepo.io/repositories/[repo-name] dist/*

The username is your email address, not __token__. More: Python Repositories, and for Poetry, uv, pixi and conda the pages under Python.

Docker (docker login)

Log in to the host alone, with your email address and the token:

echo "$CLOUDREPO_TOKEN" | docker login [org-id].mycloudrepo.io \
  --username you@example.com \
  --password-stdin

Expected: Login Succeeded. Then push and pull with the repository in the image reference:

docker tag my-app:1.0.0 [org-id].mycloudrepo.io/repositories/[repo-name]/my-app:1.0.0
docker push [org-id].mycloudrepo.io/repositories/[repo-name]/my-app:1.0.0

The password is the token, not the password you sign in to the admin portal with. docker login can also accept a user’s portal password, but use a token: it reaches only the repositories you tick, can be read only, expires, and can be revoked without changing anyone’s password. More: Docker Repositories.

When a Client Answers 401, 403 or 404

A refused credential answers 401 Unauthorized; a credential that may not do what was asked answers 403 Forbidden. A proxy repository answers 404 Not Found instead of 403, with one exception: it refuses an npm login token with 403 (see the npm item below).

npm is different for a bad token. npm sends _authToken as a Bearer token, and a token that is mistyped, expired, revoked or from another organization answers 404 Not Found, not 401. npm then prints 404 Not Found for the package. If npm reports 404 for a package you know exists, check the token first.

Check these in order:

  • The username. It must be the email address of the account that created the token, typed in lower case. Not __token__, not the token’s name, not your organization name.

  • Who created the token. Maven, Gradle, pip, twine and the other clients that send a username do not yet reliably accept a token created by the organization owner account. Create one as a user of your organization (see the note above).

  • The token. Paste it whole, starting at crp_v1_. Check on the Repository Tokens page that it has not expired or been revoked. With npm, a bad token shows as 404.

  • The repository. The token reaches only the repositories ticked when it was created. A different repository needs a token that includes it.

  • Publishing. A Read only token downloads but cannot publish; create a Read + write token for the build that publishes.

  • An npm token elsewhere. A token from npm login (crn_v1_) works only on the local npm repository it was created for, and publishes only if you chose Read-write and your user has write access. A proxy npm repository refuses it with 403. Use a token from Repository Tokens for a proxy npm repository, every other npm repository, Maven, Python and Docker.

  • The expiry date. A token stops working at 00:00 UTC on its expiry date.