# OAuth apps

Use OAuth when another product connects to a FortWatch workspace ("Connect FortWatch"), or when a tool asks for a client ID and secret. FortWatch is an OAuth 2.1 authorization server.

| | |
|---|---|
| Authorization endpoint | `https://app.fortwatch.ai/oauth/authorize` |
| Token endpoint | `https://api.fortwatch.ai/oauth/token` |
| Revocation endpoint | `https://api.fortwatch.ai/oauth/revoke` |
| Metadata (RFC 8414) | `https://api.fortwatch.ai/.well-known/oauth-authorization-server` |

## Two kinds of apps

- **Your workspace's apps.** Owners and admins create them in **Settings → API access → OAuth apps**, with a two-factor code. Only your workspace can connect them. They can also use client credentials.
- **Partner apps.** Products that work with every FortWatch customer are registered by FortWatch, after a check; people see "Verified by FortWatch" when they connect one. To register yours, contact us from [fortwatch.ai](https://fortwatch.ai/contact).

Each app has a **client ID** (`fwc_…`, public) and a **client secret** (`fws_…`, shown once; keep it on your server, never in a browser or a mobile app).

## The authorization code flow (with PKCE)

PKCE with S256 is required for every app.

**1. Send the person to the authorization endpoint:**

```text
https://app.fortwatch.ai/oauth/authorize
  ?response_type=code
  &client_id=fwc_…
  &redirect_uri=https://yourapp.example/oauth/fortwatch/callback
  &scope=assets:read issues:read
  &state=<random value you check later>
  &code_challenge=<base64url(SHA-256(code_verifier))>
  &code_challenge_method=S256
```

- `redirect_uri` must be one the app registered, **exactly** (no prefix or pattern matching).
- `scope` is a space-separated list of scopes ([Authentication](/authentication#access-scopes)); without it, the app's full access is asked. Write includes read.
- The person signs in to FortWatch if needed and sees the app, its publisher, the access asked and the workspace. Only an owner or admin can allow it, with two-factor authentication turned on: they confirm with a code from their authenticator app.
- Each person who allows the app gets a connection of their own: tokens act as whoever allowed them, and another person allowing the app later never changes them.

**2. FortWatch sends the browser back** to your `redirect_uri` with `code`, your `state` and `iss` (`https://api.fortwatch.ai`). Check that `state` is yours and `iss` is FortWatch's. If the person declined, you get `error=access_denied` instead.

**3. Exchange the code** within 60 seconds, once:

```bash
curl https://api.fortwatch.ai/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://yourapp.example/oauth/fortwatch/callback \
  -d code_verifier="$CODE_VERIFIER"
```

```json
{
  "access_token": "fwa_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "fwr_…",
  "scope": "assets:read issues:read"
}
```

The app authenticates with HTTP Basic (`client_secret_basic`, above) or with `client_id` and `client_secret` in the form (`client_secret_post`).

**4. Call the API** with the access token, exactly as with an API key:

```bash
curl https://api.fortwatch.ai/app/assets -H "Authorization: Bearer $ACCESS_TOKEN"
```

## Refresh tokens

An access token lasts an hour. Get a new one with the refresh token:

```bash
curl https://api.fortwatch.ai/oauth/token -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token -d refresh_token="$REFRESH_TOKEN"
```

Every refresh answers a **new** refresh token: store it and drop the old one. A refresh token lasts 90 days and works **once**: using one twice revokes every token of that connection (it means a token was copied), and the person has to connect the app again.

## Client credentials

A workspace's own app with **Allow client credentials** can get tokens with its client ID and secret alone, without a person signing in. Those tokens act as the app's creator and stop if that person is no longer an owner or admin. There is no refresh token: ask for a new access token when one expires.

Because these tokens act as the creator, only the creator can edit the app or replace its secret (any owner or admin can delete it). Replacing the secret or turning client credentials off stops every token they issued at once. Disconnecting the app's client credentials in **Settings → API access → Connected apps** also turns them off for the app; its creator can turn them on again.

```bash
curl https://api.fortwatch.ai/oauth/token -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=client_credentials -d scope="assets:read"
```

## What a token can do

A token acts as the person who connected the app, with only the access they allowed, and only while that person is an owner or admin of the workspace. It stops at once when the workspace disconnects the app (**Settings → API access → Connected apps**), when the app is deleted or turned off, or when the person is removed. Everything it changes is in the audit log "via OAuth app ‘<name>’".

## Revoking

```bash
curl https://api.fortwatch.ai/oauth/revoke -u "$CLIENT_ID:$CLIENT_SECRET" -d token="$TOKEN"
```

Revoking a refresh token ends every token of its connection; revoking an access token ends that token. The answer is always `200`.

## Errors

The OAuth endpoints answer OAuth's own shape, `{ "error": "…", "error_description": "…" }`: `invalid_client` (401: wrong client ID or secret), `invalid_grant` (a code or refresh token that is wrong, expired or already used), `invalid_request`, `invalid_scope`, `unauthorized_client`, `unsupported_grant_type`. Each app may call the token endpoint 60 times a minute.
