> For the complete documentation index, see [llms.txt](https://docs.nexus.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nexus.xyz/api-reference/ko/guides/authentication.md).

# 인증

네 가지 작업으로 이루어진 EVM 지갑 로그인 및 API 키 관리.

두 가지 자격 증명이 관여하며, 서로 바꿔 쓸 수 없습니다.

1. **세션 토큰.** EVM 지갑으로 고정된 메시지에 서명(EIP-191 `personal_sign`)하여 얻습니다. `Bearer` 토큰이며, **24시간** 동안 유효하고, `/keys` 엔드포인트**만** 인증합니다. 세션 토큰으로는 거래할 수 없습니다.
2. **API 키.** 세션 토큰으로 발급한 `key_id` + `secret` 쌍입니다. 모든 거래 및 계정 요청에 HMAC-SHA256을 사용해 시크릿으로 서명합니다. 시크릿은 생성 시 **한 번만** 반환되며, 저장되지 않고 다시 표시되지도 않습니다.

```
POST /auth/login   (wallet signature)         → session Bearer token
POST /keys         (Bearer)                    → key_id + secret
sign each request  (HMAC over canonical str)   → trade
```

메인 지갑을 노출하지 않는 위임 서명은 [에이전트](/api-reference/ko/guides/agent-keys.md)를 참조하십시오.

아래 모든 예시의 기본 URL은 퍼블릭 테스트넷 기본 경로인 `https://api.testnet.nexus.xyz/v1`입니다.

***

## `POST /auth/login`

EVM 지갑으로 로그인합니다.

EIP-191 `personal_sign` 서명을 제출하여 세션 토큰을 받습니다. 세션 토큰으로 `/keys` 엔드포인트를 통해 API 키를 생성하고 관리합니다. 거래에는 대신 HMAC API 키를 사용하십시오. 세션 토큰은 24시간 후 만료됩니다.

**인증:** 없음. 요청에 담긴 지갑 서명으로 요청 자체를 인증합니다.

### 요청 본문

`LoginRequest`, `application/json`, 필수.

| 필드          | 타입     | 필수 | 설명                                           |
| ----------- | ------ | -- | -------------------------------------------- |
| `message`   | string | 예  | 정확히 다음과 같아야 합니다: `Sign in to Nexus Exchange` |
| `signature` | string | 예  | EIP-191 `personal_sign` 16진수(0x 접두사, 65바이트)  |

메시지는 논스 챌린지가 아니라 고정된 문자열이며, 별도의 "챌린지 요청" 호출은 없습니다. 서버가 서명에서 지갑 주소를 *복원*하므로 주소를 보내지 않습니다.

### 응답

#### `200`

세션이 생성되었습니다. `LoginResponse`.

| 필드        | 타입     | 설명                                                   |
| --------- | ------ | ---------------------------------------------------- |
| `token`   | string | 세션 토큰(64자 16진수). `/keys` 엔드포인트의 `Bearer` 토큰으로 사용합니다. |
| `address` | string | 복원된 Ethereum 주소(0x 접두사)                              |

#### `401`

서명 검증에 실패했습니다.

### 예시

```bash
curl -X POST 'https://api.testnet.nexus.xyz/v1/auth/login' \
  -H 'Content-Type: application/json' \
  -d '{
    "message": "Sign in to Nexus Exchange",
    "signature": "0x1234...abcd"
  }'
```

응답:

```json
{
  "token": "a1b2c3d4e5f6...",
  "address": "0xAbCdEf0123456789..."
}
```

***

## `POST /keys`

API 키를 생성합니다.

인증된 지갑의 새 HMAC API 키를 생성합니다. 시크릿은 한 번만 반환됩니다. 저장되지 않으며 다시 표시되지도 않습니다. `POST /auth/login`에서 받은 세션 토큰(Bearer)이 필요합니다.

**인증:** `bearerAuth`(세션 토큰).

### 요청 본문

선택 사항. [`CreateKeyRequest`](https://docs.nexus.xyz/api-reference/guides/schemas#createkeyrequest)(영어). 본문이 없거나 비어 있으면 `{}`와 같습니다.

| 필드       | 타입      | 설명                                                                                                              |
| -------- | ------- | --------------------------------------------------------------------------------------------------------------- |
| `label`  | string  | 호출자가 지정하는 이 키의 레이블로, `GET /keys`에서 그대로 반환됩니다.                                                                   |
| `ttl_ms` | integer | 생성 시점부터 키가 유효한 기간(밀리초). 만료되지 않는 키를 원하면 생략하십시오. `[24h, 90d]`(86400000–7776000000 ms) 범위여야 하며, 그렇지 않으면 요청이 거부됩니다. |

### 응답

#### `200`

키가 생성되었습니다. [`CreateKeyResponse`](https://docs.nexus.xyz/api-reference/guides/schemas#createkeyresponse)(영어).

| 필드              | 타입              | 설명                                                                                                                                                                                                        |
| --------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `key_id`        | string          | 키 식별자. 서명된 요청에서 `X-API-Key`로 보냅니다.                                                                                                                                                                        |
| `secret`        | string          | HMAC 시크릿, 16진수. **한 번만 반환됩니다.** 즉시 저장하십시오. 복구 방법이 없습니다.                                                                                                                                                   |
| `expires_at_ms` | integer or null | 이 키의 인증이 중단되는 Unix ms 시각, 또는 만료되지 않으면 `null`. `ttl_ms`가 해석된 값입니다. **선택 사항이 아니라 null 허용:** 항상 존재합니다. 현재는 Postgres 기반 배포 환경에서만 적용됩니다. Postgres 기반이 아닌 배포 환경에서 생성한 키는 `ttl_ms`를 받아들이지만, 아직 만료된 키를 거부하지 않습니다. |

#### `400`

잘못된 요청:

| `code`             | 의미                                                                            |
| ------------------ | ----------------------------------------------------------------------------- |
| `ttl_ms_invalid`   | `ttl_ms`가 있지만 음이 아닌 정수가 아니거나, 요청 본문 자체를 읽을 수 없습니다(8KB 제한 초과 또는 유효하지 않은 JSON). |
| `ttl_out_of_range` | 형식이 올바른 `ttl_ms`가 `[24h, 90d]` 범위를 벗어납니다.                                     |

#### `401`

유효한 세션 토큰이 필요합니다.

### 예시

```bash
curl -X POST 'https://api.testnet.nexus.xyz/v1/keys' \
  -H 'Authorization: Bearer a1b2c3d4e5f6...' \
  -H 'Content-Type: application/json' \
  -d '{"label": "my trading bot", "ttl_ms": 2592000000}'
```

응답:

```json
{
  "key_id": "nx_a1b2c3d4e5f67890",
  "secret": "deadbeef...",
  "expires_at_ms": 1735689600000
}
```

새 키에는 기본적으로 **Pro** 등급이 부여됩니다. 운영자가 [`PUT /admin/tiers`](https://docs.nexus.xyz/api-reference/admin/set-tier)(영어)로 등급을 지정하며, 모든 응답은 `X-RateLimit-*` 헤더로 등급별 상한을 보고합니다.

***

## `GET /keys`

API 키 목록을 조회합니다.

인증된 지갑이 소유한 모든 키의 키 ID와 등급을 반환합니다. 시크릿은 포함되지 않습니다.

**인증:** `bearerAuth`(세션 토큰).

### 응답

#### `200`

세션의 API 키. 감싸지 않은 배열입니다. 계약은 예시를 제공하지만 이름 있는 스키마는 제공하지 않습니다.

| 필드       | 타입     | 설명                                          |
| -------- | ------ | ------------------------------------------- |
| `key_id` | string | 키 식별자                                       |
| `tier`   | string | 이 키에 지정된 요청 한도 등급(예: `Pro`, `Market Maker`) |

#### `401`

유효한 세션 토큰이 필요합니다.

### 예시

```bash
curl 'https://api.testnet.nexus.xyz/v1/keys' \
  -H 'Authorization: Bearer a1b2c3d4e5f6...'
```

응답:

```json
[
  {
    "key_id": "nx_a1b2c3d4e5f67890",
    "tier": "Pro"
  }
]
```

***

## `DELETE /keys/{key_id}`

API 키를 삭제합니다.

사용자가 소유한 키를 삭제합니다. 다른 지갑이 소유한 키는 삭제할 수 없습니다.

**인증:** `bearerAuth`(세션 토큰).

### 파라미터

| 이름       | 위치   | 타입     | 필수 | 설명        |
| -------- | ---- | ------ | -- | --------- |
| `key_id` | path | string | 예  | 삭제할 키 식별자 |

### 응답

#### `200`

키가 삭제되었습니다.

#### `401`

유효한 세션 토큰이 필요합니다.

#### `404`

키를 찾을 수 없거나 사용자 소유가 아닙니다. 소유권 확인 실패는 `403`이 아니라 `404`를 반환하므로, 다른 지갑의 키 ID를 탐색할 수 없습니다.

### 예시

```bash
curl -X DELETE 'https://api.testnet.nexus.xyz/v1/keys/nx_a1b2c3d4e5f67890' \
  -H 'Authorization: Bearer a1b2c3d4e5f6...'
```

***

## 키로 요청에 서명하기

`key_id`와 `secret`을 확보하면, 모든 `hmacAuth` 작업에 세 개의 헤더가 필요합니다.

| 헤더            | 값                                     |
| ------------- | ------------------------------------- |
| `X-API-Key`   | 키 ID(예: `nx_a1b2c3d4e5f67890`)        |
| `X-Timestamp` | Unix **밀리초** 단위의 현재 시각                |
| `X-Signature` | `hex(hmac_sha256(secret, canonical))` |

정규 문자열은 줄바꿈으로 구분된 다섯 개의 필드입니다.

```
<timestamp>\n<METHOD>\n<path>\n<query>\n<sha256_hex(body)>
```

* `path`는 호출한 전체 URL 경로가 *아니라* **계약에 적힌 그대로의** 경로입니다. `/v1` 전송 접두사는 요청이 서명을 검증하는 서비스에 도달하기 전에 제거되므로, 서명에 포함하면 **안 됩니다**. 버전 지정 경로를 호출할 때는 `/api/v1` 접두사를 포함하십시오. 이 부분은 그대로 전달되기 때문입니다.

  | 호출하는 URL                                       | 서명하는 경로           |
  | ---------------------------------------------- | ----------------- |
  | `https://api.testnet.nexus.xyz/v1/markets`     | `/markets`        |
  | `https://api.testnet.nexus.xyz/api/v1/tickers` | `/api/v1/tickers` |

  `/v1` 전송 접두사에 서명하는 것은 시계 오차에 이어 원인을 알 수 없는 `401`의 두 번째로 흔한 원인입니다.
* `query`는 원시 쿼리 문자열이며, 없으면 비워 둡니다.
* 본문이 없는 요청은 빈 문자열을 해시합니다.
* 타임스탬프는 서버 시각과 **30초** 이내여야 합니다. 시계 오차는 원인을 알 수 없는 `401`의 가장 흔한 원인입니다.

권고용 헤더인 `X-Nexus-Api-Version`과 `User-Agent`는 정규 문자열에서 **제외됩니다**.

```bash
TIMESTAMP=$(python3 -c 'import time; print(int(time.time()*1000))')
BODY_HASH=$(printf '' | shasum -a 256 | cut -d' ' -f1)
CANONICAL="$TIMESTAMP\nGET\n/markets\n\n$BODY_HASH"
SIGNATURE=$(printf '%s' "$CANONICAL" | openssl dgst -sha256 -mac HMAC \
  -macopt "hexkey:$NEXUS_API_SECRET" -hex | awk '{print $NF}')

curl 'https://api.testnet.nexus.xyz/v1/markets' \
  -H "X-API-Key: nx_a1b2c3d4e5f67890" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Signature: $SIGNATURE"
```

모든 `401` 응답은 동일한 불투명 본문 `{"code":"unauthorized"}`를 반환하므로, 키, 서명, 시계 중 무엇이 문제인지 응답으로는 알 수 없습니다. 시계 오차를 먼저 확인하고, 그다음 정규 문자열, 마지막으로 키를 확인하십시오.

> **상태:** 테스트넷 프리뷰. 자격 증명, 세션, 요청 한도 상태는 아직 게이트웨이 재시작 후 유지되지 않으므로, API 키를 다시 만들 수 있는 것으로 취급하십시오.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nexus.xyz/api-reference/ko/guides/authentication.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
