# API Token Management

Base URL: `${FORGEJO_URL}/api/v1`

> **Note:** Token management endpoints require **Basic Authentication** (username:password), not token-based auth. This is a security measure to prevent token escalation.

## List Tokens

```
GET /users/{username}/tokens
```

**Parameters:**
| Name | Type | In | Description |
|------|------|------|-------------|
| page | integer | query | Page number (1-based) |
| limit | integer | query | Page size |

```bash
curl -s -u "username:password" \
  "${FORGEJO_URL}/api/v1/users/{username}/tokens"
```

**Response:** Array of `AccessToken` objects:
| Field | Type | Description |
|-------|------|-------------|
| id | integer | Token ID |
| name | string | Token name |
| sha1 | string | Token hash (only shown on creation) |
| scopes | array | Granted scopes |
| token_last_eight | string | Last 8 characters of token |

---

## Create Token

```
POST /users/{username}/tokens
```

**Request Body (CreateAccessTokenOption):**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| name | string | yes | Token name (must be unique per user) |
| scopes | array | no | List of scopes to grant |

### Available Scopes

| Scope | Description |
|-------|-------------|
| `all` | Full access (all scopes) |
| `read:activitypub` | Read ActivityPub data |
| `write:activitypub` | Write ActivityPub data |
| `read:issue` | Read issues |
| `write:issue` | Write issues |
| `read:misc` | Read miscellaneous data |
| `write:misc` | Write miscellaneous data |
| `read:notification` | Read notifications |
| `write:notification` | Write notifications |
| `read:organization` | Read organization data |
| `write:organization` | Write organization data |
| `read:package` | Read packages |
| `write:package` | Write packages |
| `read:repository` | Read repository data |
| `write:repository` | Write repository data |
| `read:user` | Read user data |
| `write:user` | Write user data |
| `read:admin` | Read admin data |
| `write:admin` | Write admin data |

### Create a full-access token

```bash
curl -s -X POST -u "username:password" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{token_name}",
    "scopes": ["all"]
  }' \
  "${FORGEJO_URL}/api/v1/users/{username}/tokens"
```

### Create a read-only token

```bash
curl -s -X POST -u "username:password" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{token_name}",
    "scopes": [
      "read:repository",
      "read:issue",
      "read:organization"
    ]
  }' \
  "${FORGEJO_URL}/api/v1/users/{username}/tokens"
```

### Create a CI/CD token

```bash
curl -s -X POST -u "username:password" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{token_name}",
    "scopes": [
      "read:repository",
      "write:repository",
      "read:package",
      "write:package"
    ]
  }' \
  "${FORGEJO_URL}/api/v1/users/{username}/tokens"
```

**Response on creation:**
```json
{
  "id": 1,
  "name": "{token_name}",
  "sha1": "{full_token}",
  "scopes": ["read:repository", "write:repository", "read:package", "write:package"],
  "token_last_eight": "f456..."
}
```

> **Important:** The `sha1` field (the actual token value) is **only returned once** at creation time. Store it securely immediately.

---

## Delete Token

```
DELETE /users/{username}/tokens/{token}
```

The `{token}` path parameter can be either the token ID (integer) or the token name (string).

### Delete by name

```bash
curl -s -X DELETE -u "username:password" \
  "${FORGEJO_URL}/api/v1/users/{username}/tokens/{token_name}"
```

### Delete by ID

```bash
curl -s -X DELETE -u "username:password" \
  "${FORGEJO_URL}/api/v1/users/{username}/tokens/{token_id}"
```

Returns `204 No Content` on success.

---

## Token Naming Conventions

Use descriptive names that indicate purpose and scope:

| Pattern | Example | Use Case |
|---------|---------|----------|
| `ci-{service}-{repo}` | `ci-actions-myapp` | CI/CD pipeline |
| `deploy-{env}` | `deploy-staging` | Deployment automation |
| `bot-{name}` | `bot-issue-labeler` | Automated bots |
| `dev-{machine}` | `dev-laptop` | Local development |
| `readonly-{purpose}` | `readonly-monitoring` | Read-only integrations |

---

## Token Rotation Strategy

Tokens should be rotated periodically. A rotation workflow:

```bash
# 1. Create new token
NEW_TOKEN=$(curl -s -X POST -u "username:password" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{token_name}",
    "scopes": ["read:repository", "write:repository"]
  }' \
  "${FORGEJO_URL}/api/v1/users/{username}/tokens" | jq -r '.sha1')

# 2. Update your CI/CD secrets with the new token value
echo "New token: $NEW_TOKEN"

# 3. Verify the new token works
curl -s -H "Authorization: token $NEW_TOKEN" \
  "${FORGEJO_URL}/api/v1/user"

# 4. Delete the old token
curl -s -X DELETE -u "username:password" \
  "${FORGEJO_URL}/api/v1/users/{username}/tokens/{token_name}"
```

---

## Using Tokens in CI/CD

### Forgejo Actions

In `.forgejo/workflows/*.yml`, use repository secrets:

```yaml
steps:
  - name: API call
    env:
      FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
    run: |
      curl -s -H "Authorization: token $FORGEJO_TOKEN" \
        "${FORGEJO_URL}/api/v1/repos/${{ github.repository }}"
```

### Environment variable

```bash
export FORGEJO_PAT="your-token-here"
curl -s -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/user"
```

### Git credential helper

```bash
# Configure git to use token for HTTPS
git config --global url."https://oauth2:${FORGEJO_PAT}@{FORGEJO_HOST}/".insteadOf \
  "https://{FORGEJO_HOST}/"
```
