# 인증

퍼스톡 OpenAPI는 **OAuth 2.0 Client Credentials Grant**를 씁니다. API 키(`client_id` + `client_secret`)로 1시간짜리 access token을 받고, 모든 요청의 `Authorization` 헤더에 실어 보내요.

```text
[API 키 발급 (웹, 1회)] → [POST /oauth2/token] → access_token (1시간)
                                                    ↓
                               Authorization: Bearer <access_token>
```

## API 키 발급

1. 퍼스톡에 Google 계정으로 로그인해요.
2. [프로필 → OpenAPI 탭](https://furstock.trade/profile?tab=api)에서 키 이름을 적고 **새 키 발급**을 눌러요.
3. 화면에 나온 `client_id`(`fsk_`로 시작)와 `client_secret`(`fss_`로 시작)을 복사해요.

| 규칙        | 내용                                                                                                           |
| ----------- | -------------------------------------------------------------------------------------------------------------- |
| secret 노출 | 발급 직후 **한 번만** 보여요. 서버엔 해시만 저장돼서 다시 볼 수 없어요. 잃어버렸으면 폐기하고 새로 발급하세요. |
| 개수        | 계정당 활성 키 최대 **5개**                                                                                    |
| 범위        | 키는 **발급한 본인 계정**만 조작해요. 다른 사람 계정을 대신 조작하는 방법은 없어요.                            |

## access token 받기

`POST https://api.furstock.trade/oauth2/token`

OAuth 2.0 표준대로 `application/x-www-form-urlencoded`로 보내요. `application/json`도 받습니다.

| 필드            | 필수 | 설명                      |
| --------------- | ---- | ------------------------- |
| `grant_type`    | ✅   | 항상 `client_credentials` |
| `client_id`     | ✅   | 발급받은 client_id        |
| `client_secret` | ✅   | 발급받은 client_secret    |

```bash
curl -s https://api.furstock.trade/oauth2/token \
  -d grant_type=client_credentials \
  -d client_id=$FURSTOCK_CLIENT_ID \
  -d client_secret=$FURSTOCK_CLIENT_SECRET
```

**응답 200**

```json
{ "access_token": "eyJhbGciOiJIUzI1NiIs...", "token_type": "Bearer", "expires_in": 3600 }
```

| 상태 | `error`           | 의미                                  |
| ---- | ----------------- | ------------------------------------- |
| 400  | `invalid_request` | 필드 누락·형식 오류                   |
| 401  | `invalid_client`  | client_id/secret이 틀렸거나 폐기된 키 |
| 429  | `rate_limited`    | 같은 client_id로 분당 30회 초과       |

토큰에는 refresh token이 없어요. 만료(`expires_in`초) 몇 분 전에 같은 요청으로 새 토큰을 받으세요. 요청마다 토큰을 새로 받지는 마세요. 분당 30회 제한에 걸립니다.

## API 호출

모든 요청에 헤더를 붙여요.

```http
GET /v1/accounts/holdings HTTP/1.1
Host: api.furstock.trade
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

시세·종목·커뮤니티 조회처럼 공개된 API는 토큰 없이도 호출돼요. 각 API 표의 **인증** 열을 보세요.

토큰이 유효한지는 `GET /v1/me`로 확인할 수 있어요.

```json
{ "userId": "8b6b74e7-2bf7-42ad-8cfc-534ed05708b8", "isAdmin": false, "authMethod": "api_key" }
```

## 키 폐기

[프로필 → OpenAPI 탭](https://furstock.trade/profile?tab=api)에서 **폐기**를 누르면:

- 그 키로 새 토큰을 받을 수 없어요(`401 invalid_client`).
- **이미 받은 토큰도 즉시 막혀요.** 폐기 후 요청은 `401`이에요. 드물게 최대 30초까지 늦어질 수 있어요.
- 되살릴 수 없어요. 필요하면 새로 발급하세요.

## API 키로 할 수 없는 것

키가 유출돼도 피해가 커지지 않도록, 다음은 **퍼스톡 웹에 로그인한 상태에서만** 할 수 있어요. API 키 토큰으로 호출하면 `403 session_required`가 와요.

| 작업                                     | 이유                                                |
| ---------------------------------------- | --------------------------------------------------- |
| API 키 발급·목록·폐기 (`/v1/api-keys`)   | 유출된 키로 새 키를 만들어 계속 쓰는 걸 막기 위해   |
| 회원 탈퇴 (`POST /v1/accounts/withdraw`) | 되돌릴 수 없는 작업이라서                           |
| 관리자 권한                              | 관리자 계정의 키라도 API에선 일반 사용자로 취급돼요 |

## 보안 수칙

- `client_secret`은 코드에 직접 쓰지 말고 환경변수나 비밀 저장소로 관리하세요. `.env`는 `.gitignore`에 넣으세요.
- 브라우저에서 동작하는 커스텀 클라이언트라면 secret이 사용자에게 그대로 노출돼요. 본인만 쓰는 도구에만 쓰고, 공개 웹앱에 secret을 넣지 마세요.
- 봇마다 키를 따로 발급하면 문제가 생긴 봇만 골라서 폐기할 수 있어요.
