# Authentication

> Personal keys for your own scripts, Sign in with Refract for apps, and the device code for TVs and terminals.

Every request acts for one member, and carries one bearer token in the `Authorization` header:

```http
GET /v1/me HTTP/1.1
Host: getrefract.app
Authorization: Bearer rfk_…
```

| Who runs the code | Use | Token lifetime |
|---|---|---|
| You, for yourself | A personal key | Until you revoke it, or the expiry you chose |
| Your app, for other members | Sign in with Refract (OAuth 2.1, PKCE) | Access token one hour, refresh token 90 days |
| A device without a keyboard | The device code | Same as OAuth |

The API is open to a first group of members while it is new. A member outside it cannot create keys or approve
apps, and the API refuses tokens issued for them.

## Scopes

A credential carries the scopes the member granted. Each endpoint needs one, named in the
[reference](/developers/reference); a credential without it gets `403` with
[`INSUFFICIENT_SCOPE`](/developers/errors/insufficient-scope).

| Scope | Lets your app |
|---|---|
| `profile:read` | See the member's profile: username, display name, avatar. |
| `library:read` | See the library and the watchlist. |
| `history:read` | See what the member watched and when. |
| `ratings:read` | See the member's ratings. |
| `lists:read` | See the member's lists. |

The matching `:write` scopes exist and imply read; the API serves no write yet.

## Personal keys

For code that only you run. In the Refract app, open **Access and connected apps** from the Integrations screen
and create a key: name it, pick its scopes, and optionally give it an expiry between 1 and 365 days. The key starts
with `rfk_`, carries a checksum, and is shown once. A member can hold up to 10 keys and revoke any of them from
the same screen; a revoked key stops working at once.

Never ship a personal key in an app other people install: it opens your own account.

## Sign in with Refract

For apps that other members use. Refract is an OAuth 2.1 authorization server with PKCE (S256 only), refresh token
rotation and OpenID Connect.

**Register your app** in the same Access screen, under your apps. Give it a name and its redirect URIs:
`https://` addresses, loopback `http://127.0.0.1` or `http://[::1]` for desktop tools, or a reverse-domain scheme
for a native app (`com.example.app:/callback`). Choose a public client (no secret; PKCE protects the exchange) for
apps that run on the user's device, or a confidential client (with a secret you can rotate) for a server.

**The flow.** Send the member to the authorization endpoint with a fresh PKCE verifier:

```
https://getrefract.app/api/auth/oauth2/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://example.com/callback
  &scope=profile:read library:read offline_access
  &state=RANDOM_STATE
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256
  &resource=https://getrefract.app/v1
```

The member signs in to Refract, sees your app's name and the scopes in plain words, and approves or refuses. Refract
redirects back with `code` and your `state`. Check the `state`, then exchange the code:

```sh
curl https://getrefract.app/api/auth/oauth2/token \
  -d grant_type=authorization_code \
  -d code=THE_CODE \
  -d redirect_uri=https://example.com/callback \
  -d client_id=YOUR_CLIENT_ID \
  -d code_verifier=THE_VERIFIER \
  -d resource=https://getrefract.app/v1
```

The answer holds an `access_token` for the API (one hour) and, if you asked for `offline_access`, a
`refresh_token`. Each refresh returns a new refresh token and retires the old one; store the new one before using it.
Refresh tokens last 90 days. A member can disconnect your app at any time from their Access screen; your next refresh
then fails and your access token stops working when it expires.

Send `resource=https://getrefract.app/v1` (RFC 8707) when you ask for tokens: it names the API the token is for.

## TVs, consoles and terminals

The device authorization grant (RFC 8628), for devices where typing a password is painful:

1. Your device asks for a code:
   ```sh
   curl https://getrefract.app/api/auth/device/code \
     -d client_id=YOUR_CLIENT_ID \
     -d "scope=profile:read library:read offline_access" \
     -d resource=https://getrefract.app/v1
   ```
2. Show the `user_code` and ask the member to open **getrefract.app/device** on their phone. They sign in, check
   the app name and the scopes, and approve.
3. Meanwhile, poll the token endpoint every `interval` seconds:
   ```sh
   curl https://getrefract.app/api/auth/oauth2/token \
     -d grant_type=urn:ietf:params:oauth:grant-type:device_code \
     -d device_code=THE_DEVICE_CODE \
     -d client_id=YOUR_CLIENT_ID
   ```
   `authorization_pending` means keep waiting; `slow_down` means wait longer between polls.

## Discovery

| Document | Address |
|---|---|
| Authorization server metadata | `https://getrefract.app/.well-known/oauth-authorization-server` |
| OpenID Connect configuration | `https://getrefract.app/.well-known/openid-configuration` |
| The API's protected-resource metadata (RFC 9728) | `https://getrefract.app/.well-known/oauth-protected-resource/v1` |

A `401` from the API carries a `WWW-Authenticate` header that points to the protected-resource metadata and names
the scope the endpoint needs.
