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

# Workload Identity for GitLab CI/CD

This guide explains how to configure Workload Identity for GitLab CI/CD to authenticate with Bytebase without storing long-lived credentials.

## Step 1: Create a Workload Identity in Bytebase

1. Go to **IAM & Admin** > **Users & Groups**.
2. Click **Add User** in the upper-right corner.
3. Select **Workload Identity** as the Type.
4. Fill in the configuration:

| Field                     | Description                                                                | Example                 |
| ------------------------- | -------------------------------------------------------------------------- | ----------------------- |
| **Name**                  | Display name for this identity                                             | `GitLab CI Deploy`      |
| **Email**                 | Unique email prefix (automatically appended with `@workload.bytebase.com`) | `gitlab-ci-deploy`      |
| **Platform**              | Select GitLab CI                                                           | `GitLab CI`             |
| **Group / Username**      | GitLab group or username (required)                                        | `my-group`              |
| **Project**               | Project name (leave empty to allow all projects)                           | `my-project`            |
| **Allowed Branches/Tags** | Select branch/tag restrictions                                             | `All branches and tags` |
| **Roles**                 | Assign workspace roles                                                     | `GitOps Service Agent`  |

5. Click **Confirm** to create the Workload Identity.

## Step 2: Assign Roles

After creating the Workload Identity, assign the `GitOps Service Agent` role to enable automated CI/CD workflows:

1. Go to your project's **Settings** > **Members**.
2. Click **Grant Access**.
3. Enter the Workload Identity email (e.g., `gitlab-ci-deploy@workload.bytebase.com`).
4. Select the **GitOps Service Agent** role.
5. Click **Confirm**.

<Tip>
  The `GitOps Service Agent` role is designed for automated CI/CD workflows, allowing the identity to create and execute database changes. See [Roles and Permissions](/administration/roles) for details.
</Tip>

## Step 3: Configure GitLab CI/CD Pipeline

In your GitLab CI/CD pipeline, add the following configuration:

### Request OIDC Token

Add `id_tokens` configuration to get the JWT token from GitLab:

```yaml theme={null}
stages:
  - deploy

deploy-database:
  stage: deploy
  image: alpine:latest
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  variables:
    BYTEBASE_URL: https://bytebase.example.com
    WORKLOAD_IDENTITY_EMAIL: gitlab-ci-deploy@workload.bytebase.com
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      # Exchange GitLab OIDC token for Bytebase API token
      RESPONSE=$(curl -s -X POST "${BYTEBASE_URL}/v1/auth:exchangeToken" \
        -H "Content-Type: application/json" \
        -d "{\"token\": \"${GITLAB_OIDC_TOKEN}\", \"email\": \"${WORKLOAD_IDENTITY_EMAIL}\"}")

      ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r '.accessToken')
      if [ "$ACCESS_TOKEN" = "null" ] || [ -z "$ACCESS_TOKEN" ]; then
        echo "Failed to get access token"
        echo "$RESPONSE"
        exit 1
      fi

      # Verify the token by calling the user info API
      USER_INFO=$(curl -s "${BYTEBASE_URL}/v1/users/me" \
        -H "Authorization: Bearer $ACCESS_TOKEN")
      echo "Authenticated as: $USER_INFO"
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
```

## Complete Example

Here's a complete GitOps workflow that uses Workload Identity to deploy database migrations:

```yaml theme={null}
# .gitlab-ci.yml
stages:
  - review
  - deploy

variables:
  BYTEBASE_URL: https://bytebase.example.com
  WORKLOAD_IDENTITY_EMAIL: gitlab-deploy@workload.bytebase.com

# SQL Review on merge requests
sql-review:
  stage: review
  image: bytebase/sql-review-action:latest
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  script:
    - |
      # Exchange OIDC token for Bytebase token
      export BYTEBASE_TOKEN=$(curl -s -X POST "${BYTEBASE_URL}/v1/auth:exchangeToken" \
        -H "Content-Type: application/json" \
        -d "{\"token\": \"${GITLAB_OIDC_TOKEN}\", \"email\": \"${WORKLOAD_IDENTITY_EMAIL}\"}" \
        | jq -r '.accessToken')

      # Run SQL review
      sql-review --url ${BYTEBASE_URL} --token ${BYTEBASE_TOKEN} \
        --file-pattern "migrations/**/*.sql"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      changes:
        - migrations/**

# Deploy on merge to main
rollout:
  stage: deploy
  image: bytebase/bytebase-action:latest
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  script:
    - |
      # Exchange OIDC token for Bytebase token
      export BYTEBASE_TOKEN=$(curl -s -X POST "${BYTEBASE_URL}/v1/auth:exchangeToken" \
        -H "Content-Type: application/json" \
        -d "{\"token\": \"${GITLAB_OIDC_TOKEN}\", \"email\": \"${WORKLOAD_IDENTITY_EMAIL}\"}" \
        | jq -r '.accessToken')

      # Create release and rollout
      bytebase-action rollout \
        --url ${BYTEBASE_URL} \
        --token ${BYTEBASE_TOKEN} \
        --file-pattern "migrations/**/*.sql" \
        --project projects/my-project \
        --targets instances/prod/databases/mydb
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      changes:
        - migrations/**
```

<Tip>
  For more details on GitOps workflows, see [GitOps Overview](/gitops/overview) and [Migration-Based Workflow](/gitops/migration-based-workflow/overview).
</Tip>

## Self-Hosted GitLab

For self-hosted GitLab instances, update the audience (`aud`) to match your GitLab instance URL:

```yaml theme={null}
id_tokens:
  GITLAB_OIDC_TOKEN:
    aud: https://gitlab.your-company.com
```

When creating the Workload Identity in Bytebase, ensure the configuration matches your self-hosted GitLab instance.

## Troubleshooting

### Token Exchange Fails

If the token exchange returns an error:

1. **Verify the project path and branch**: Check that your pipeline's project path and branch match the configured values in Bytebase.

2. **Check the audience**: Ensure the `aud` in your `id_tokens` configuration matches your GitLab instance URL (e.g., `https://gitlab.com` for GitLab.com).

3. **Verify OIDC is enabled**: GitLab CI/CD OIDC tokens require GitLab 15.7 or later.

### Permission Denied

If API calls return permission errors:

1. Verify the Workload Identity has the `GitOps Service Agent` role assigned.
2. Check that the Workload Identity is a member of the target project.

### Debug Token Claims

To inspect the OIDC token claims, decode the JWT:

```yaml theme={null}
script:
  - |
    echo "$GITLAB_OIDC_TOKEN" | cut -d. -f2 | base64 -d | jq .
```

This shows the token's claims including `sub`, `aud`, `namespace_path`, `project_path`, and `ref` that Bytebase validates.
