# InsForge auth.md

You are an agent. InsForge offers two ways to authenticate a user and obtain a
credential to call the API. Pick the one your situation supports; both match or
create a real user by verified email (same email = same account).

1. **Device flow (RFC 8628, human-in-the-loop)** — usable TODAY, no upstream
   issuer required. The user approves a short code in their browser. Issues a
   scoped OAuth `access_token` (+ `refresh_token`). Best when a human is
   available to approve. Same flow as `insforge login --device`.
2. **ID-JAG identity assertion** — your agent host platform mints a WorkOS
   ID-JAG for the user's verified email. Requires your platform to be a WorkOS
   ID-JAG issuer (most are not yet). Issues a `uak_` InsForge user API key.

InsForge does NOT support anonymous registration, verified-email registration,
or claim flows.

## Step 1 - Discover

```http
GET https://api.insforge.dev/.well-known/oauth-protected-resource
```

Use `resource` as the `aud` when minting an ID-JAG. Then fetch the
authorization-server metadata named by `agent_auth_uri`:

```http
GET https://api.insforge.dev/.well-known/oauth-authorization-server
```

Use only the methods/credential types listed under `agent_auth`.

## Method A - Device flow (recommended when a human can approve)

Easiest: have the user run `npx @insforge/cli@latest login --device` and
approve — the CLI then stores and refreshes the credential itself. To drive the
flow yourself:

1. Request a device code (public InsForge CLI client, no secret needed):

   ```http
   POST https://api.insforge.dev/api/oauth/v1/device_authorization
   Content-Type: application/x-www-form-urlencoded

   client_id=clf_NK8cMUs41gm8ZcfdtSguVw&scope=projects:read%20projects:write
   ```

   `projects:read projects:write` is enough to create and manage a project
   (a new user gets a personal organization automatically, so no
   `organizations:*` scope is needed). Add `user:read` if you want `whoami`
   to read the account back. This client's scopes are `user:read`,
   `organizations:read`, `projects:read`, `projects:write`; request only the
   ones you need.

   -> `{ device_code, user_code, verification_uri, verification_uri_complete, expires_in, interval }`

2. Show the user `verification_uri_complete`. They open it, sign in to InsForge,
   and click Approve — the consent screen lists the scopes you requested.
   (human in the loop) Ask them to sign in with Google or GitHub if they can:
   that email counts as verified immediately, so they can create a project right
   away. Email+password signup needs a separate verification round and can hit
   signup protections (captcha, per-IP and per-domain caps).

3. Poll the token endpoint, honoring `interval`:

   ```http
   POST https://api.insforge.dev/api/oauth/v1/token
   Content-Type: application/x-www-form-urlencoded

   grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=<device_code>&client_id=clf_NK8cMUs41gm8ZcfdtSguVw
   ```

   -> `400 { "error": "authorization_pending" }` while waiting — keep polling at `interval`.
   -> `400 { "error": "slow_down" }` means you polled too fast. Per RFC 8628
      §3.5 you MUST add 5 seconds to your polling interval for this request AND
      every subsequent one (the increase is permanent, not a one-off backoff).
   -> `200 { access_token, refresh_token, expires_in, scope }` once approved.

4. You now have a credential. Hand it to the CLI (Step 3) — that is how you
   provision a project and get the guidance to drive InsForge.

## Method B - Register with ID-JAG

Mint a WorkOS ID-JAG with:

- `aud` = the `resource` from the protected-resource metadata
- `iss` = `https://auth.workos.bot`
- `typ` = `oauth-id-jag+jwt`, `alg` = `RS256`
- `sub` = stable WorkOS subject for the user
- `email` = the user's email, with `email_verified: true` (required)
- fresh `jti`, near-term `exp` (~5 minutes)

```http
POST https://api.insforge.dev/agent/auth
Content-Type: application/json

{
  "type": "identity_assertion",
  "assertion_type": "urn:ietf:params:oauth:token-type:id-jag",
  "assertion": "<your ID-JAG JWT>",
  "requested_credential_type": "api_key"
}
```

The response includes `user_id`, `api_key` (== `credential`, a `uak_` user
API key) and `scopes`. This logs you in to the InsForge cloud backend as a real
user (matched by verified email; a later sign-in with the same email is the same
account). It does NOT provision a project — continue with Step 3.

## Step 3 - Use the credential (InsForge CLI)

Authenticate the CLI with the credential from Step 2, then provision a project.
Always invoke it as `npx @insforge/cli@latest ...` — plain `npx @insforge/cli`
can resolve to a stale global install that rejects newer flags with
`unknown option`.

```bash
# 1. Give the CLI your credential
export INSFORGE_ACCESS_TOKEN=<access_token>   # Method A (an OAuth access token)
# or, if you hold a uak_ from Method B:
#   npx @insforge/cli@latest login --user-api-key <uak_...>

# 2. Provision and link a project (as the authenticated user)
npx @insforge/cli@latest create
npx @insforge/cli@latest link --project-id <project-id>
npx @insforge/cli@latest whoami               # confirm which account you are

# 3. Work on the project
npx @insforge/cli@latest deployments deploy
```

To confirm identity, use `whoami` (it calls the API with your live token), not
`current` (which reports the CLI's locally-stored login and may not match the
token you passed in `INSFORGE_ACCESS_TOKEN`). `whoami` needs the `user:read`
scope — include it in step 1's `scope` if you want to read the account back.

**`link` is the important step for you as an agent:** besides writing the
project's own key into `.insforge/project.json`, it installs the official
InsForge agent skills, so the remaining commands (`db`, `storage`,
`functions`, `deploy`, `compute`, ...) come with guidance you can follow. Run
`npx @insforge/cli@latest metadata` to see what the project already has.

**Token lifetime (Method A).** The CLI reads `INSFORGE_ACCESS_TOKEN` but never
refreshes a token supplied that way, and it expires in ~1h. Either refresh it
yourself and re-export (`grant_type=refresh_token`; the `refresh_token` lasts
30 days and is not rotated), or let the CLI own login and refresh instead:
`npx @insforge/cli@latest login --device` prints the same approval link and
stores the credential itself. A Method B `uak_` never expires, so it needs
neither.

**Two credential layers — don't confuse them.** The credential above is the
*platform-level* login: it authorizes create-org / create-project /
fetch-the-project's-access-key. Project-level work — deploy, compute, SQL,
storage, functions — runs on the *project's own key* that `link` wrote into
`.insforge/project.json`; the CLI picks that up automatically.

Or call the cloud API directly with `Authorization: Bearer <credential>`.

## Errors

OAuth-style JSON `{ "error", "error_description" }`. Codes:
`invalid_request`, `unsupported_credential_type`, `invalid_signature`,
`invalid_audience`, `expired`, `invalid_issuer`, `missing_verified_email`,
`replay_detected`, `invalid_assertion`, `server_error`. On any of these, mint a
fresh ID-JAG and retry; do not replay the same assertion.
