APIアクセス制限
Prexは事業者の認証サーバと連携し、APIアクセス制限を行うことができます。
APIアクセス制限の目的
Getting Startedのセクションで作成したアプリでは、APIキーが公開されているため、誰でもアクセスできる状態になっています。Prexはノンカストディアルウォレットなので、ユーザーの資産は安全に保たれます。しかし、APIキーが公開されていると、正当な開発者以外の人が勝手にPrexの機能を使ってしまい、ガス代が増えるリスクがあります。
この問題を解決するには、Prex APIへのアクセスを制限することが大切です。具体的には、サービス提供者の認証システムとPrexウォレットを連携させて、認証されたユーザーだけがPrex APIを使えるようにする必要があります。
APIアクセス制限のフロー
認証を実現するために、事業者の認証サーバでAPIキーを動的に生成します。このユーザーごとに生成されたAPIキーを「IDトークン」と呼びます。IDトークンには、ユーザーIDが含まれています。これにより、「サービス提供者の認証システム上のユーザー」と「Prex上のウォレット」が1対1で対応することができます。
事業者は、事前準備として非対称鍵を作成します。秘密鍵は事業者の認証サーバで保持し、公開鍵をPrexダッシュボードに登録します。秘密鍵はIDトークンに署名するために使用され、公開鍵はIDトークンを検証するために使用されます。
- 事業者の認証サーバで、ユーザの認証を行 う。
- 事業者の認証サーバでユーザのセッションを確認し、IDトークンを生成する
- 事業者のフロントエンドアプリでIDトークンを受け取り、IDトークンをAPIキーとしてAPIリクエストを行う
1-3のフローを以下に図示します。
実装手順
1. 鍵ペアの生成
開発者はP-256曲線で非対称鍵を生成します。 形式はJson Web Key(JWK)です。 この鍵はウォレットの管理とは関係ありません。 ユーザのウォレットは、各ユーザのパスキーによって管理されています。
# 秘密鍵を生成します。EC(楕円曲線)アルゴリズムを使用し、P-256曲線を指定します。
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 > private_key.pem
# 生成された秘 密鍵から公開鍵を抽出し、公開鍵ファイル(PEM形式)に保存します。
openssl pkey -pubout -in private_key.pem > public_key.pem
# Ecklesというnpmパッケージをインストールします。
# これにより、PEM形式の鍵をJWK(JSON Web Key)形式に変換できます。
npm i -g eckles
# PEM形式の公開鍵と秘密鍵をそれぞれをJWK形式に変換します。
eckles public_key.pem > public_key.jwk
eckles private_key.pem > private_key.jwk
作成したpublic_key.jwk
を、Prexダッシュボードの「ポリシー設定」にて登録します。
APIアクセス制限を、「API認証あり」に変更してください。
2. IDトークンの生成(事業者の認証サーバ)
private_key.jwk
を使用して、IDトークンを生成します。
import { SignJWT } from "jose";
const POLICY_ID = '8873b874-55e5-44c5-b91d-ec257dbd0173';
const PRIVATE_KEY = // 'from private_key.jwk'
async function generateIdTokenForUser(userId: string) {
const jwt = await new SignJWT()
.setProtectedHeader({ alg: "ES256" })
.setIssuedAt()
.setExpirationTime("7d")
.setAudience(POLICY_ID.toString())
.setSubject(userId)
.sign(PRIVATE_KEY);
return jwt;
}
3. IDトークンのセット(事業者のフロントエンド)
ポリシーIDをコピーして、下記コードのPOLICY_IDに設定します。
import { PrexUIKitProvider } from "@prex0/uikit";
function App() {
return (
<PrexUIKitProvider
chainId={421614}
policyId={POLICY_ID}
>
<WalletComponent />
</PrexUIKitProvider>
);
}
ウォレット作成のための、WalletComponentを作成します。
import { AuthStatus, EmbeddedWallet } from "@prex0/uikit/wallet";
import { Address } from "@prex0/uikit/identity";
function LoginComponent() {
// 独自のログイン画面
return <div>ログイン</div>
}
function WalletComponent() {
const getIDToken = useCallback(async () => {
// 事業者のバックエンドからIDトークンを取得
// ...
})
return (<AuthStatus getIDToken={getIDToken} loginComponent={LoginComponent}>
<EmbeddedWallet>
<Address />
</EmbeddedWallet>
</AuthStatus>);
}
コード全体を確認するには、サンプルプロジェクトもご覧ください。
ウォレットの作成と移行のシーケンス図
注意点
- JWTの生成には非対称鍵暗号を使用し、公開鍵をPrexの管理画面に登録する必要があります。
- JWTのペイロードには、audienceとしてポリシーID、subjectとして事業者のバックエンドでのユーザーIDを指定します。
- 認証を使用しない場合でも、Bot対策としてreCAPTCHAの使用を推奨します。
- Prexはあくまでもセルフカストディウォレットであるため、ユーザのパスキーがなければウォレット内の資産にアクセスすることはできません。つまりユーザ認証が、ウォレット内の資産へのアクセスを保証するわけではありません。
実装例については、認証付き送金アプリケーションを参照してください。