# Authentication

> API tokens and signing in from the command line.

Source: <https://frontieror-challenge.com/docs/authentication>

## API tokens

The API and the starter's `frontieror` command sign in with a personal API token.

- Create one on the dashboard under **Settings → Security → Generate token**. It is shown once, so copy it then.
- A token starts with `fo_`. It acts as you, on your team.
- You have one token at a time. Generating a new one, or signing in with `frontieror init`, replaces it, and the old one stops working at once.
- Tokens don't expire. Keep yours out of code you share, and replace it if it leaks.

## Using a token

Send it in the `Authorization` header of every request:

```python
import os

import httpx

api = httpx.Client(
    base_url="https://frontieror-challenge.com/api/v1",
    headers={"Authorization": f"Bearer {os.environ['FRONTIEROR_TOKEN']}"},
)
print(api.get("/me").json())
```

A missing, wrong or replaced token gets `401` with “Missing or invalid API token.”

## Signing in from the starter

`uv run frontieror init` opens your browser at `/device` with a code. Check that it matches the code in your terminal, then **Approve**. The command receives a new token and saves it as `FRONTIEROR_TOKEN` in `.env`. The code is valid for 10 minutes.

To use an existing token instead, run `uv run frontieror init --token fo_…`, or set `FRONTIEROR_TOKEN` in the environment or in `.env`.

> Never approve a code that someone else sent you: approving gives them a token for your account.

## Signing in from your own tool

The same browser sign-in works for any command-line tool, following the OAuth device flow (RFC 8628). Only the client id `frontieror-cli` is accepted.

1. `POST /api/v1/device/code` with `client_id=frontieror-cli`, form-encoded or as JSON. The response has `device_code`, `user_code`, `verification_uri`, `verification_uri_complete`, `expires_in` (600 seconds) and `interval` (5 seconds).
2. Open `verification_uri_complete` for the user, who approves the code while signed in.
3. Every `interval` seconds, `POST /api/v1/device/token` with `grant_type=urn:ietf:params:oauth:grant-type:device_code`, the `device_code` and the `client_id`.

Until the user decides, the token request returns `400` with `{"error": "authorization_pending"}`. It ends with one of:

| Response                                                 | Meaning                                                                        |
| -------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `200` `{"access_token": "fo_…", "token_type": "Bearer"}` | Approved. The token is handed out once and replaces the user's previous token. |
| `400` `access_denied`                                    | The user denied the sign-in.                                                   |
| `400` `expired_token`                                    | The code expired before it was approved.                                       |
| `400` `invalid_grant`                                    | The device code is unknown or was already used.                                |
