<!-- Source: https://docs.geopera.com/api-reference/authentication · Markdown for LLMs -->

# Authentication

Every request to the Geopera API carries a **Bearer token** in the `Authorization`
header:

```
Authorization: Bearer <token>
```

There are two ways to get one — an **OAuth2 / OIDC** flow for human users and
service clients, and an **API key** for machine-to-machine integrations. Both resolve
to a *principal* with a set of *scopes*, and every operation checks the scope it needs
before running.

## OAuth2 / OIDC token endpoint

The platform exposes a standards-compliant OpenID Connect token endpoint, so most
OAuth2 client libraries and existing geospatial tooling work without modification:

```
POST https://api.geopera.com/realms/public/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
```

Tokens are short-lived JWTs (5 minutes), with a longer-lived refresh token (30
minutes). The discovery document, JWKS, `userinfo`, `introspect`, `revoke`, and
`logout` endpoints live under the same `/realms/public/protocol/openid-connect/` base.

### Grant: `password`

Exchange a user's credentials for a token:

```bash
curl -X POST https://api.geopera.com/realms/public/protocol/openid-connect/token \
  -d grant_type=password \
  -d username='you@example.com' \
  -d password='••••••••'
```

### Grant: `client_credentials` (machine-to-machine)

Exchange an API key (as the client secret) for a token — the right flow for backend
services and automation:

```bash
curl -X POST https://api.geopera.com/realms/public/protocol/openid-connect/token \
  -d grant_type=client_credentials \
  -d client_id=geopera \
  -d client_secret='<your-api-key>'
```

### Grant: `refresh_token`

Exchange a refresh token for a fresh access token without re-sending credentials:

```bash
curl -X POST https://api.geopera.com/realms/public/protocol/openid-connect/token \
  -d grant_type=refresh_token \
  -d refresh_token='<refresh-token>'
```

### Token response

```json
{
  "access_token": "eyJhbGciOiJIUzI1Ni
...",
  "token_type": "Bearer",
  "expires_in": 300,
  "refresh_token": "...",
  "refresh_expires_in": 1800,
  "scope": "..."
}
```

## API keys

API keys are issued per organization and are ideal for integrations that act on
behalf of the organization rather than a single user. An organization administrator
creates a key with the [`api_keys.create`](/api-reference/operations) operation; the
key is shown **once** at creation time — store it securely.

A key can be presented two ways:

- As the `client_secret` in the `client_credentials` grant above (recommended — you
  get a short-lived JWT back), or
- Directly as a Bearer token / `X-API-Key` header for SDK and tooling conventions.

Each key carries a permission set (read / write / process) that maps to the scopes
its requests are allowed to use. Keys can be revoked at any time with
[`api_keys.revoke`](/api-reference/operations).

## Scopes

Authorization is expressed in a single currency: **`resource:action`** scopes. Each
operation declares the one scope it requires, and the check runs identically no matter
how the operation is invoked. Examples:

| Scope | Grants |
|---|---|
| `catalog:read` | Search the imagery catalog |
| `orders:read` / `orders:write` | View / place and manage orders |
| `items:read` / `items:write` | Read / create, update, delete STAC items & assets |
| `processing:process` | Create and dispatch processing jobs |
| `billing:read` / `billing:write` | View balance & history / top up, manage payment methods |
| `uploads:write` | Upload bring-your-own data |
| `shares:write` | Create and revoke share links |

A wildcard form is honoured where appropriate: `items:*` grants every action on
`items`, and `*:read` grants the read action across resources. The
[Operations Reference](/api-reference/operations) lists the exact scope each
operation requires.

> Scope is the coarse gate. Within an operation, fine-grained ownership is still
> enforced — e.g. you can only read items in projects your organization is a member
> of, regardless of holding `items:read`.

## Principals

Every authenticated request resolves to a **principal** of one kind. The kind
determines how scopes are derived:

| Kind | Used by | Authority |
|---|---|---|
| `human` | A logged-in user (OIDC `password` grant) | The self-serve scope set for the user's organization |
| `api_key` | An organization API key | The key's read / write / process permissions |
| `service` | Internal/system callers | Full trust (not customer-facing) |
| `worker` | Processing workers | A narrow, job-bound scope — register their own job's outputs and nothing else |

All actions are attributed to a principal, and every write is recorded with its
principal for audit and provenance.

## Errors

- `401 Unauthenticated` — missing or invalid token.
- `403 Forbidden` — authenticated, but the principal lacks the operation's scope.

Both are returned as [`application/problem+json`](/api-reference/errors).
