> ## Documentation Index
> Fetch the complete documentation index at: https://starforge.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 认证

> 拿到 bearer token、保持有效，并选对凭据种类。

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -X POST https://starforge.your-company.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "password": "..."}'
```

```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": { "username": "alice", "role": "user" }
}
```

之后每个请求都带上 `Authorization: Bearer <token>`。

## 四种凭据

它们不能互换，用错种类是最常见的对接问题。

<ParamField path="Access token" type="JWT，8 小时">
  `POST /api/auth/login` 和 CLI 设备流返回的东西，代表**一个人**。里面带角色，所以管理员的 token
  能打开管理接口。你写脚本代表自己操作时用的就是它。
</ParamField>

<ParamField path="Refresh token" type="30 天">
  和 access token 一起签发，但**只**通过 `httpOnly` Cookie 下发，不进响应体——浏览器里的 JavaScript
  读不到它。`POST /api/auth/refresh` 用它换新的 access token。命令行客户端不用它，改走设备流。
</ParamField>

<ParamField path="Ingest token" type="30 天，绑定单次运行">
  以 `STARFORGE_TOKEN` 注入训练容器，只对一次 run 有效，也只有 `/api/ingest/*` 认它。作业手里只有这个，
  永远拿不到你的账号凭据。见[从训练代码回传](/zh-Hans/api-reference/ingest)。
</ParamField>

<ParamField path="Deployment token" type="可吊销，不过期">
  只授权访问某一个[模型部署](/zh-Hans/api-reference/inference)，别的都不行。它独立于任何登录会话，
  所以创建它的人离职后服务照常运行。在控制台吊销。
</ParamField>

<Info>
  角色只有 `admin` 和 `operator`（控制台上把后者显示为「Member」）。`operator` 的 token 打管理接口返回 `403`，不是 `401`——后者会让人误以为
  token 有问题。
</Info>

## 从浏览器或脚本登录

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
SF_TOKEN=$(curl -sX POST https://starforge.your-company.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "password": "'"$SF_PASSWORD"'"}' | jq -r .token)
```

每个 IP 每分钟 10 次。启用了 OIDC 的部署会关掉这条路由，先用 `GET /api/auth/config` 看看这套部署
支持哪些登录方式，别想当然。

## 从没有浏览器的机器登录

设备流，RFC 8628。`sf login --device-flow` 走的就是这条路，SSH 会话和构建机上应该用它。

<Steps>
  <Step title="申请验证码">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    curl -sX POST https://starforge.your-company.com/api/cli/device/code \
      -H "Content-Type: application/json" -d '{}'
    ```

    ```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    {
      "device_code": "5f3a...",
      "user_code": "WDJB-MJHT",
      "verification_uri": "https://starforge.your-company.com/cli/device",
      "verification_uri_complete": "https://starforge.your-company.com/cli/device?user_code=WDJB-MJHT"
    }
    ```
  </Step>

  <Step title="在浏览器里批准">
    在任何一台已登录的设备上打开 `verification_uri_complete`。
  </Step>

  <Step title="轮询直到批准">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    curl -sX POST https://starforge.your-company.com/api/cli/device/token \
      -H "Content-Type: application/json" \
      -d '{"device_code": "5f3a..."}'
    ```

    未批准时返回 `authorization_pending`，批准后返回 access token 和 `expires_in`。
    轮询间隔不要低于 5 秒，上限是每分钟 20 次。
  </Step>
</Steps>

## 确认成功

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -H "Authorization: Bearer $SF_TOKEN" \
  https://starforge.your-company.com/api/auth/me
```

返回用户名、角色和配额。这里拿到 `401`，说明 token 没带、格式不对或已过期——先重新登录，再去查别的。

## 过期怎么办

access token 默认 8 小时（部署侧 `FORGE_JWT_HOURS` 控制）。过期后接口返回 `401`。按你的身份分两种恢复方式：

* **浏览器会话**：带 refresh Cookie 调 `POST /api/auth/refresh`。
* **脚本或 CI**：重新登录，或重跑设备流。不要跨次运行缓存 token 然后赌它还能用；
  先看 `GET /api/auth/me`，遇到 `401` 就重新认证。

`POST /api/auth/logout` 会吊销 refresh token；部署配了 Redis 时，access token 也会立即失效，
而不是等到自然过期。

<Accordion title="为什么 refresh token 从不出现在响应体里">
  JavaScript 能读到的 token，注入的脚本也能偷走，而 30 天的 refresh token 比 8 小时的 access token
  值钱得多。放进 `httpOnly` Cookie，意味着一个 XSS 漏洞的代价是一次会话，而不是一个月的访问权。
  这同时也意味着命令行客户端根本用不了它——设备流因此是一条独立设计的路径，而不是补丁。
</Accordion>
