認証
マッチングエンジンは、スパム防止と注文の所有権確立のため、すべての書き込みエンドポイント(注文の発注、マーケットの作成、支払いリクエスト)で認証を要求します。読み取り専用エンドポイント(オーダーブックのスナップショット、マーケットメタデータ、価格フィード)は認証不要で公開されています。エンドポイントごとの詳細はOpenAPI仕様(openapi.yaml)を参照してください。
現在の認証スキームはNIP-98 HTTP Authです。将来的には別の認証方法も提供予定です
メインキーとエフェメラルキー
Section titled “メインキーとエフェメラルキー”Nostrベースの認証を使用する場合、2種類の鍵の区別を理解することが重要です。
メインNostr鍵は、HTTPエンドポイントでのNIP-98認証に使用される長期的なIDです。マッチングエンジンはこの公開鍵を、注文の所有権管理とレート制限のためのユーザーIDとして使用します。
エフェメラル(一時)鍵は、注文ごとに生成される新しいsecp256k1鍵ペアです。公開鍵部分は、02または03のプレフィックスバイトに続いてX座標を表す64文字の小文字16進数からなる、33バイトの圧縮形式で注文とともにマッチングエンジンに送信されます。これはsecp256k1.getPublicKey(privkey, true)が返すのと同じ形式であり、BIP-340由来の32バイトx-only形式を使うメインのNostr鍵とは意図的に異なります。マッチングエンジンは送信時にこの圧縮形式を検証するため、誤ってx-only公開鍵を渡すと400が返ります。マッチ後、エンジンは両当事者のエフェメラル公開鍵を中継し、暗号化されたアトミックスワップ通信のためのECDH共有秘密を確立できるようにします。取引相手がメインのNostr IDを知ることはありません — 見えるのはエフェメラルキーのみです。
リクエストボディのバインディング
Section titled “リクエストボディのバインディング”ボディを伴うメソッド(POST、PUT、PATCH)の場合、NIP-98イベントにはpayloadタグを含める必要があります。その値は、リクエストボディのバイト列に対する小文字16進数のSHA-256ダイジェストです。マッチングエンジンは実際に受信したバイトからダイジェストを再計算し、値が一致しない場合はリクエストを拒否します。このバインディングがなければ、キャプチャされたトークンを60秒のフレッシュネスウィンドウ内に同じURLとメソッドで改竄されたボディに対して再生でき、ユーザーが署名していない注文を発注できてしまいます。
multipartボディの場合、クライアントが生成するmultipartバウンダリを含む、実際に送信されるバイトをハッシュ化してください。最も確実なのは、一時的なRequestオブジェクトを介してボディを事前シリアライズし、そのarrayBuffer()を取得してハッシュ化したうえで、同じバイトを同じContent-Typeヘッダーで送信する方法です。fetchに送信時のバウンダリを任せると、ハッシュ値がサーバーが受信するバイトと一致しなくなります。
GET、DELETE、SignalRのネゴシエーション呼び出しはボディを持たないため、payloadタグは省略されます。1 MiBを超えるボディはダイジェスト計算前に拒否されるため、クライアントはユーザー制御の入力(特にマーケットのサムネイル画像)をこの上限以下に制限する必要があります。
匿名性について
Section titled “匿名性について”永続的なメインキーを使用するため、マッチングエンジンはそのキーに紐づく取引履歴を原理的には追跡できます。しかし実際にはこれは軽微な問題です:
- ユーザーは取引済みトークンを帯域外で送信できる — 決済後のトークンに対してマッチングエンジンは制御を持ちません。
- 取引相手がメインキーを知ることはありません。やり取りはすべてエフェメラルキーを通じて行われます。
- LN-authなどの将来の認証方式は、永続的なNostrキーを不要にすることで、より強い匿名性を提供します。
エラーレスポンス
Section titled “エラーレスポンス”| ステータス | 意味 |
|---|---|
| 401 Unauthorized | 認証トークンが欠落、不正、または期限切れ |
| 403 Forbidden | 有効なトークンだが、認証されたユーザーがリソースを所有していない |