> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Authenticating the Aspect CLI

> Sign in to the Aspect CLI with aspect auth login and generate an ASPECT_API_TOKEN for CI so authenticated CLI features can post results back to GitHub or GitLab.

Some Aspect CLI features post results back to your version-control host as your CI runs. They authenticate to the **Aspect API**, which in turn calls your VCS through the Aspect App installed on your organization.

This page covers the **provider-agnostic** half of that setup: creating an Aspect account, signing in locally, and generating the `ASPECT_API_TOKEN` that CI uses. Installing the App that talks to your VCS is provider-specific:

<CardGroup cols={2}>
  <Card title="GitHub App" icon="github" href="/docs/cli/authentication-github">
    Install and link the Aspect Workflows GitHub App, then enable status checks, the PR task summary comment, and PR lint comments.
  </Card>

  <Card title="GitLab App" icon="gitlab" href="/docs/cli/authentication-gitlab">
    Link the Aspect Workflows GitLab App, then enable commit statuses, rolling MR notes, and MR lint comments.
  </Card>
</CardGroup>

## Do I need to authenticate?

Only if you use a CLI feature that talks to the Aspect API. Plain commands — `aspect test //...` locally, or `aspect build //...` against a remote cache — work with no auth at all, so you can skip this page if none of the features below apply.

The features that require authentication, by provider:

| Provider | Feature                         | What it does                                                                                                                                                                  |
| -------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GitHub   | `GithubStatusChecks`            | Per-task pass/fail status checks on the commit under review.                                                                                                                  |
| GitHub   | `GithubStatusComments`          | A single PR task summary comment aggregating every task's live status.                                                                                                        |
| GitHub   | `GithubLintComments`            | Inline `aspect lint` findings as PR review comments.                                                                                                                          |
| GitHub   | `aspect format` / `aspect lint` | Backs up changed-file detection with the GitHub PR Files API when a local `git diff` can't resolve the diff base (e.g. shallow clones). Optional — `git diff` is the default. |
| GitLab   | `GitlabCommitStatuses`          | Per-task pass/fail status on the commit (the MR's commit status column).                                                                                                      |
| GitLab   | `GitlabStatusComments`          | A single rolling MR note aggregating every task's live status.                                                                                                                |
| GitLab   | `GitlabLintComments`            | Inline `aspect lint` findings as position-bound MR discussions.                                                                                                               |

When authentication is unavailable, every feature above **skips gracefully** — the underlying `aspect` command continues normally and logs a single line explaining what was missing.

## Setup overview

Authentication always involves the same two pieces, regardless of host:

1. **Install and link the Aspect App** for your VCS organization — one-time per org. See the [GitHub App](/docs/cli/authentication-github) or [GitLab App](/docs/cli/authentication-gitlab) page.
2. **Generate an `ASPECT_API_TOKEN`** so CI runners can authenticate non-interactively — covered below.

Both are scoped to an **Aspect account**: whenever the CLI authenticates (on CI via `ASPECT_API_TOKEN`, or locally via `aspect auth login`), it does so as your Aspect account.

<Note>
  Don't have an account? Sign up for free at [signup.aspect.build](https://signup.aspect.build/). A free account works across all Aspect products and unlocks our [free Bazel courses](/learning).

  The first user to sign up is automatically granted the **Account Admin** role. Subsequent users joining the same account need an existing admin to assign them an appropriate role in the [Aspect admin portal](https://auth.aspect.build/oauth/portal/users) before they can install or link an App — **GitHub Integration Admin** for the GitHub App, **GitLab Integration Admin** for the GitLab App, or **Account Admin** for both.
</Note>

## Generate an Aspect API token (for CI)

CI runners are non-interactive, so they authenticate with a long-lived token instead of a browser flow.

<Steps>
  <Step title="Create the token">
    Open the [API Tokens portal](https://auth.aspect.build/oauth/portal/api-tokens) and select **Generate Token**. In the dialog:

    * **Description** — any label that identifies where the token will be used (e.g. `ASPECT_API_TOKEN`, `ci-main`).
    * **Roles** — select the **GitHub Integration** and/or **GitLab Integration** roles your CI needs. The role gates which VCS scopes tasks and features can use — see [Token roles and scopes](#token-roles-and-scopes) below. For most CI use cases pick **GitHub CI** and/or **GitLab CI**.
    * **Expiry** — the portal's <code>This token will expire</code> field sets how long the token stays valid. Pick a lifetime that matches your secret-rotation policy; you'll need to generate a new token and update CI when it expires.
  </Step>

  <Step title="Copy the secret">
    After you select **Create**, the portal shows a **Client ID** and **Secret Key**.

    <Warning>
      The Secret Key is shown **once** and never again. Copy it immediately — if you lose it you'll need to generate a new token.
    </Warning>

    The value of `ASPECT_API_TOKEN` is the two strings joined with a colon:

    ```
    <CLIENT_ID>:<SECRET_KEY>
    ```
  </Step>

  <Step title="Provide the token to CI">
    Store `ASPECT_API_TOKEN` as a secret in your CI platform and expose it to the runner. How you wire it in depends on the platform:

    <CodeGroup>
      ```yaml GitHub Actions theme={null}
      jobs:
        aspect-task:
          runs-on: ubuntu-latest
          permissions:
            id-token: write    # required for ArtifactUpload (and silences setup-aspect's id-token warning)
          steps:
            - uses: actions/checkout@v6
            - uses: aspect-build/setup-aspect@2306377a61c45954ab2df7c7311698b109364352 # v2026.26.9
              with:
                aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}
            - run: aspect <task> //...
      ```

      ```yaml GitLab theme={null}
      aspect-task:
        rules:
          - if: $CI_PIPELINE_SOURCE == "merge_request_event"
        script:
          - aspect <task> //...
      # Set ASPECT_API_TOKEN as a masked CI/CD variable in Settings → CI/CD → Variables.
      ```

      ```yaml Buildkite theme={null}
      steps:
        - label: "Aspect task"
          command: aspect <task> //...
      ```

      ```yaml CircleCI theme={null}
      jobs:
        aspect-task:
          docker:
            - image: cimg/base:current
          steps:
            - checkout
            - run: aspect <task> //...
      # Set ASPECT_API_TOKEN as a project environment variable or context secret.
      ```
    </CodeGroup>

    <Tip>
      **On GitHub Actions, prefer the `with: aspect-api-token:` input over `env: ASPECT_API_TOKEN:`.** The [`aspect-build/setup-aspect`](https://github.com/aspect-build/setup-aspect) action receives the long-lived `<CLIENT_ID>:<SECRET>` only within its own step's process, pipes it via stdin to `aspect auth login --with-api-token`, and persists the resulting short-lived JWT on disk. Downstream steps see only the JWT — the long-lived token is **never written to `GITHUB_ENV`**, so any other action you call in the same job (including untrusted third-party ones that read `process.env`) cannot exfiltrate it.

      Setting the token as a job-level `env:` exposes the raw secret to every step in the job, which is the failure mode the input-based pattern is designed to eliminate. **On Buildkite, store the token as a [Buildkite secret](https://buildkite.com/docs/pipelines/security/secrets/buildkite-secrets) named exactly `ASPECT_API_TOKEN`** — the CLI fetches it directly from the secret store (via `buildkite-agent secret get`) when the env var is unset, so the raw token never enters the job environment. The GitLab and CircleCI examples above use a CI/CD variable because no equivalent mechanism exists yet for those platforms.
    </Tip>
  </Step>
</Steps>

## Token roles and scopes

`ASPECT_API_TOKEN` does not itself carry any VCS permissions. It authenticates to the Aspect API, and when a feature needs to call your VCS the Aspect API uses the linked App to make the call. Which scopes a feature can use is determined by the **roles** assigned to your token in the [API Tokens portal](https://auth.aspect.build/oauth/portal/api-tokens), bounded by the App's own permissions (so a feature can never receive more access than the App was granted at install time).

Roles are bundles of fine-grained permissions. Pick the smallest bundle that covers what your CI actually does, or pick the **Integration User** role for everything.

### GitHub token roles and scopes

| Role                          | GitHub installation token scope(s)                                          | Typical use                                                                  |
| ----------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `GitHub CI`                   | `checks: write`, `statuses: write`, `pull_requests: write`, `actions: read` | Recommended default for CI runners. Covers status checks and PR comments.    |
| `GitHub Integration User`     | All scopes below                                                            | Full access — equivalent to every `GitHub Token:` role combined.             |
| `GitHub Token: checks`        | `checks: read`, `checks: write`                                             | `GithubStatusChecks`.                                                        |
| `GitHub Token: statuses`      | `statuses: read`, `statuses: write`                                         | Commit status APIs.                                                          |
| `GitHub Token: pull requests` | `pull_requests: read`, `pull_requests: write`                               | `GithubStatusComments`, `GithubLintComments`, and other PR-comment features. |
| `GitHub Token: issues`        | `issues: read`                                                              | Read-only issue access.                                                      |
| `GitHub Token: actions`       | `actions: read`                                                             | Read-only GitHub Actions API access.                                         |

<Note>
  **Write implies read** — assigning a `*.write` role automatically authorizes the matching `*.read` scope, so you don't need to add both. `metadata: read` is always included on every token.
</Note>

### GitLab token roles and scopes

| Role                                   | GitLab token scope | Typical use                                                                                                          |
| -------------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------- |
| `GitLab CI`                            | `api`              | Recommended default for CI runners. Covers `GitlabCommitStatuses`, `GitlabStatusComments`, and `GitlabLintComments`. |
| `GitLab Integration User`              | `api`, `read_api`  | Full access — equivalent to every `GitLab Token:` role combined.                                                     |
| `GitLab Token: api`                    | `api`              | Posting commit statuses, creating/updating MR notes, anything else under GitLab's broad `api` scope.                 |
| `GitLab Token: external status checks` | `api`              | Posting External Status Check responses on MRs (Premium/Ultimate tier only).                                         |
| `GitLab Token: MR notes`               | `api`              | Creating and updating merge request notes.                                                                           |
| `GitLab Token: MR read`                | `read_api`         | Read-only access to merge request metadata + changes.                                                                |

<Note>
  Unlike the GitHub roles, GitLab token roles do **not** auto-imply each other. GitLab's OAuth scope vocabulary is coarser than GitHub's per-resource permission map, so role narrowing applies primarily as a permission gate; the minted token's effective scope set is whatever the App was registered with for your role's GitLab scope (today: `api read_api`).
</Note>

### When a role is missing

If a feature needs a scope your token's roles don't cover, the Aspect API returns 403 and the feature skips gracefully — the affected feature logs `authentication failed for <owner>/<repo> — <reason>`. Add the missing role in the API Tokens portal and re-issue the token to enable the scope.

## Local development

None of the built-in Aspect CLI tasks currently need authentication when run on a developer machine — `aspect <task>` works locally without any auth setup. Future CLI features that interact with the Aspect API from your machine will, and when they ship you'll authenticate by running:

```shell theme={null}
aspect auth login
```

This walks through a device OAuth flow in your browser and stores credentials under `~/.aspect/credentials.json` (mode `0600`) so subsequent `aspect` invocations pick them up automatically. The flow needs an interactive browser session and does **not** work on CI runners; CI must always use `ASPECT_API_TOKEN`.

Related commands:

* `aspect auth logout` — remove stored credentials for the current profile.
* `aspect auth login --profile <name>` — sign in under a named credential profile (defaults to `default`), useful when switching between Aspect accounts.

## Troubleshooting

If authentication is unavailable (missing credentials, App not installed/linked, or not a PR/MR context), authenticating features skip their work and the underlying `aspect` command continues normally. They log `authentication failed for <owner>/<repo> — <reason>` explaining what was missing.

If PR comments, MR notes, or status checks aren't appearing as expected, check the App installation and `ASPECT_API_TOKEN` first — see the [GitHub App](/docs/cli/authentication-github#troubleshooting) or [GitLab App](/docs/cli/authentication-gitlab#troubleshooting) page for provider-specific symptoms.

## Support

Hit a bug or have a feature request? Open an issue on [aspect-build/aspect-cli](https://github.com/aspect-build/aspect-cli/issues). For questions or to chat with the team and other users, join our community Slack at [slack.aspect.build](https://slack.aspect.build).
