---
title: Authentication
description: Authenticate with the GitLite API. Create access tokens, send them in the Authorization header, and choose token scopes.
url: https://pr-1-12825562dfc9.thally.app/authentication
lastVerified: 2026-09-15T00:00:00.000Z
verifiedVersion: GitLite 1.27.3
---

# Authentication

Authenticate with the GitLite API. Create access tokens, send them in the Authorization header, and choose token scopes.

Most GitLite API endpoints require an access token. You create a token for your
user, grant it scopes, and send it with every request.

## Send a token in the Authorization header

Put the word `Bearer`, a space, and the token value in the `Authorization` header:

```text
Authorization: Bearer 9a7f0c2e...
```

The examples in these docs read the complete header value from `GITLITE_AUTH`:

```bash
export GITLITE_AUTH="Bearer $GITLITE_TOKEN"
```

#### curl

    ```bash
    curl -s http://localhost:3000/api/v1/user \
      -H "Authorization: $GITLITE_AUTH"
    ```

#### JavaScript

    ```js
    const res = await fetch('http://localhost:3000/api/v1/user', {
      headers: { Authorization: process.env.GITLITE_AUTH },
    })
    console.log(res.status, (await res.json()).login)
    ```

The API spec describes this scheme as `AuthorizationHeaderToken`: "API tokens must
be prepended with `Bearer` followed by a space." Both `Bearer` and `token` prefixes
are case-insensitive, but the `token` prefix is **deprecated**. Both prefixes still
authenticate the request; however, requests that use the `token` prefix receive
three extra response headers:

| Header | Value |
| --- | --- |
| `Deprecation` | `true` |
| `Sunset` | `Wed, 01 Apr 2026 00:00:00 GMT` |
| `Warning` | `299 - "The 'token' authorization scheme is deprecated; use 'Authorization: Bearer <token>'"` |

Operators can reject the `token` prefix entirely by setting
`[api] ALLOW_LEGACY_TOKEN_SCHEME = false` in `app.ini`. When disabled, requests
using the `token` prefix return `401` with the message
`"the 'token' authorization scheme is disabled; use 'Authorization: Bearer <token>'"`.
See [Configuration: `[api]`](/reference/configuration-api) for details.

Requests without credentials to an endpoint that requires them return `401` with
`"message": "token is required"`.

## Create a token with the API

Create a token with HTTP basic authentication, using your username and password.

#### curl

    ```bash
    curl -s -X POST "http://localhost:3000/api/v1/users/$GITLITE_USER/tokens" \
      -u "$GITLITE_USER:$GITLITE_PASSWORD" \
      -H "Content-Type: application/json" \
      -d '{"name": "ci-token", "scopes": ["read:repository", "write:issue"]}'
    ```

#### JavaScript

    ```js
    const { GITLITE_USER, GITLITE_PASSWORD } = process.env
    const basic = Buffer.from(`${GITLITE_USER}:${GITLITE_PASSWORD}`).toString('base64')
    const res = await fetch(`http://localhost:3000/api/v1/users/${GITLITE_USER}/tokens`, {
      method: 'POST',
      headers: { Authorization: `Basic ${basic}`, 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: 'ci-token-js', scopes: ['read:repository', 'write:issue'] }),
    })
    console.log(res.status, (await res.json()).token_last_eight)
    ```

A `201 Created` response includes `sha1`, the token value. GitLite never returns
it again; later reads show only `token_last_eight`. Token names must be unique per
user, so a second request with the same `name` returns `400`.

If your account has two-factor authentication enabled, also send the current
one-time code in the `X-GITEA-OTP` header.

## Choose scopes

A token can only reach the API categories its scopes allow. Each category has a
`read:` and a `write:` scope, and `write:` includes `read:`.

| Category | Scopes | Covers |
| --- | --- | --- |
| `activitypub` | `read:activitypub`, `write:activitypub` | ActivityPub endpoints |
| `admin` | `read:admin`, `write:admin` | `/admin` endpoints; requires a site admin |
| `issue` | `read:issue`, `write:issue` | Issues, pull request comments, labels, milestones |
| `notification` | `read:notification`, `write:notification` | Notifications |
| `organization` | `read:organization`, `write:organization` | Organizations and teams |
| `package` | `read:package`, `write:package` | Package registry |
| `repository` | `read:repository`, `write:repository` | Repositories, files, branches, hooks |
| `user` | `read:user`, `write:user` | The authenticated user and their settings |

Two special scopes exist:

- `all` grants every category at `write` level.
- `public-only` restricts the token to public repositories and organizations,
  combined with the other scopes you grant.

A request outside a token's scopes returns `403` and names the missing scope:

```json
{
  "message": "token does not have at least one of required scope(s), required=[write:user], token scope=read:user",
  "url": "http://localhost:3000/api/swagger"
}
```

## Other authentication methods

| Method | How to send it | Notes |
| --- | --- | --- |
| Basic authentication | `curl -u user:password` | Supported for API calls, but prefer tokens. |
| Query parameter | `?token=` or `?access_token=` | Accepted by default and marked deprecated in the API spec. Operators can reject it with `[security] DISABLE_QUERY_AUTH_TOKEN = true`. Avoid it: URLs end up in proxy and server logs. |
| Sudo | `Sudo: <username>` header or `?sudo=` | Site admins only. Performs the request as another user. |

## Next steps

- [Handling errors](/guides/errors) explains every `401` and `403` body.
- [Pagination](/pagination) shows how to read long lists with your token.