ListenHubSDKs & CLI
JavaScript SDK

認証

サーバーサイドの処理では API キーで、サインイン中のユーザーの代理として動作する場合は OAuth ユーザートークンで SDK を認証します。

SDK には 2 つのクライアントがあり、それぞれ認証方法が異なります。コードが動作する場所に合うほうを選んでください。

OpenAPIClientListenHubClient
認証情報API キー(lh_sk_…OAuth ユーザーアクセストークン
送信されるヘッダーAuthorization: Bearer <apiKey>Authorization: Bearer <accessToken>
実行主体自分のアカウント / キーの所有者サインイン中のユーザー
動作環境サーバー、スクリプト、CIユーザー向けアプリ
Base URLhttps://api.marswave.ai/openapihttps://api.listenhub.ai/api

API キーはアカウントへのフルアクセス権を持つ機密情報です。サーバー上に保管してください。ブラウザ、モバイル、その他あらゆるクライアントサイドのバンドルにキーを含めて配布しないでください。ユーザー向けアプリでは OAuth を使う ListenHubClient を利用し、各リクエストがユーザー自身のアカウントで実行されるようにします。

API キー(OpenAPIClient

サーバーサイドや自動化の処理には OpenAPIClient を使います。公開されている OpenAPI のインターフェースを対象とし、アカウントを表す 1 つの API キーで認証します。

キーを作成する

ダッシュボードの listenhub.ai/settings/api-keys でキーを生成します。キーの形式は lh_sk_<keyId>_<secret> です。secret は作成時に一度だけ表示されます — その場でコピーし、自分のサーバーだけが読める場所に保管してください。

クライアントを構築する

キーを明示的に渡すか、LISTENHUB_API_KEY を設定して引数なしで構築します。どちらもない場合、コンストラクタは例外を投げます。

import { OpenAPIClient } from '@marswave/listenhub-sdk';

// Reads process.env.LISTENHUB_API_KEY
const client = new OpenAPIClient();

const { items } = await client.listSpeakers({ language: 'en' });

実行時にキーを環境変数に入れます:

LISTENHUB_API_KEY=lh_sk_... node app.js
import { OpenAPIClient } from '@marswave/listenhub-sdk';

const client = new OpenAPIClient({
  apiKey: process.env.LISTENHUB_API_KEY, // load from your secret store
});

キーはハードコードせず、シークレットマネージャーや環境変数から読み取ることを優先してください。上の例も環境変数から取得しています — コミットするソースコードに lh_sk_… のリテラル文字列を貼り付けないでください。

クライアントはリクエストごとに Authorization: Bearer <apiKey> を自動で設定します。更新が必要なトークンはありません — キーはローテーションまたは削除するまで有効です。

生の HTTP での等価な呼び出し

SDK を使わない場合は、OpenAPI の base URL に対して同じヘッダーを送信します:

curl https://api.marswave.ai/openapi/v1/speakers/list?language=en \
  -H "Authorization: Bearer $LISTENHUB_API_KEY"

キーをローテーションする

キーが漏洩した場合は、ダッシュボードで削除して新しいキーを発行します。削除は即座に有効になります — 古いキーを使い続けているリクエストは認証エラーで失敗するようになります(エラー処理 を参照)。差し替えたキーをシークレットストアに反映し、再デプロイしてください。

OAuth ユーザートークン(ListenHubClient

各リクエストを特定のサインイン中ユーザーとして実行しなければならない場合は ListenHubClient を使います — たとえば、ユーザーが自分の ListenHub アカウントでログインするデスクトップツールや CLI です。このクライアントが持つのは API キーではなく、短いブラウザフローで取得する OAuth の access token です。

フロー

ListenHubClient は、標準的な authorize → exchange → refresh → revoke のライフサイクルに対応する 4 つの認証メソッドを提供します:

メソッド用途
connectInit({ callbackPort })ログインセッションを開始します。sessionId と、ブラウザで開く authUrl を返します。
connectToken({ sessionId, code })コールバックの code をトークンと交換します。accessTokenrefreshTokenexpiresIn を返します。
refresh({ refreshToken })現在のアクセストークンが期限切れになったときに新しいトークンを発行します。同じ形のトークンを返します。
revoke({ refreshToken })リフレッシュトークンを無効化します(サインアウト)。

ログイン用のクライアント自体には認証情報が不要なので、素の ListenHubClient を構築して connectInit / connectToken を実行します。フローは次のとおりです:

OAuth のコールバックを受け取るローカルサーバーを起動し、そのポートを指定して connectInit を呼び出します。返ってきた authUrl をユーザーのブラウザで開きます。

ユーザーがブラウザで認可します。ListenHub は http://127.0.0.1:<callbackPort>/?code=<code> にリダイレクトします。そのリクエストから code を読み取ります。

connectToken({ sessionId, code }) を呼び出し、code を accessTokenrefreshTokenexpiresIn(アクセストークンが期限切れになるまでの秒数)と交換します。両方のトークンを永続化します。

アクセストークンで認証済みのクライアントを構築します:new ListenHubClient({ accessToken })

import * as http from 'node:http';
import { ListenHubClient } from '@marswave/listenhub-sdk';

// 1. Login client needs no credentials
const loginClient = new ListenHubClient();

// 2. Stand up a temporary callback server, then start the session
const { port, codePromise, server } = startCallbackServer();
const { authUrl, sessionId } = await loginClient.connectInit({ callbackPort: port });

const open = (await import('open')).default;
await open(authUrl); // user signs in here

// 3. Exchange the callback code for tokens
const code = await codePromise;
const tokens = await loginClient.connectToken({ sessionId, code });
server.close();

// tokens: { accessToken, refreshToken, expiresIn }
// Persist tokens somewhere private to the user's machine.

// 4. Build an authenticated client
const client = new ListenHubClient({ accessToken: tokens.accessToken });
const me = await client.getCurrentUser();

コールバックサーバーは数行の node:http で済みます — 一時的なポートを待ち受け、リダイレクトが届いたら resolve します:

function startCallbackServer() {
  let resolveCode!: (code: string) => void;
  const codePromise = new Promise<string>((r) => (resolveCode = r));

  const server = http.createServer((req, res) => {
    const code = new URL(req.url!, 'http://localhost').searchParams.get('code');
    if (code) {
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end('<h1>Login successful! You can close this tab.</h1>');
      resolveCode(code);
    } else {
      res.writeHead(400).end('Missing code');
    }
  });

  // Port 0 → OS assigns a free port
  server.listen(0, '127.0.0.1');
  const port = (server.address() as { port: number }).port;
  return { port, codePromise, server };
}

トークンを最新に保つ

アクセストークンは expiresIn 秒後に期限切れになります。認証状態を保つ方法は 2 つあります。

静的なトークンを渡す — クライアントが短命な場合(1 回のスクリプト実行、1 バッチのリクエスト):

const client = new ListenHubClient({ accessToken: tokens.accessToken });

ゲッターを渡す — クライアントが長命な場合。accessToken() => string | undefined を受け付け、SDK がリクエストごとにそれを呼び出します。現在のトークンを変数に保持し、別経路で更新しておけば、ゲッターは常に最新の値を返します:

let current = tokens;

const client = new ListenHubClient({
  // Called before each request — return the freshest token you hold
  accessToken: () => current.accessToken,
});

// Refresh on a timer (or lazily, just before expiry) and swap it in
async function refreshTokens() {
  current = await loginClient.refresh({ refreshToken: current.refreshToken });
}

refresh は新しい accessToken 新しい refreshToken を返します。両方を永続化してください — 最新の refreshToken を保存しておけば、再起動をまたいでユーザーのサインイン状態が保たれ、次回の更新にも使えます。

サインアウトする

リフレッシュトークンを指定して revoke を呼び出し、サーバー側でセッションを無効化してから、ローカルに保存したトークンを破棄します:

await loginClient.revoke({ refreshToken: current.refreshToken });
// Then delete accessToken + refreshToken from local storage.

認証情報の保存場所

  • API キー はサーバーの環境変数またはシークレットマネージャーに置き、process.env.LISTENHUB_API_KEY から読み取ります。ブラウザに渡ってはならず、ソース管理にコミットしてもいけません。
  • OAuth トークン はユーザーごとのものです。accessTokenrefreshToken は、そのユーザーのセッションに閉じた場所に保存してください — CLI やデスクトップツールなら、ユーザーの設定ディレクトリ配下の 0600 権限のファイルが適しています。refreshToken は機密情報として扱ってください:失効するまで、新しいアクセストークンを発行し続けられます。

エラー処理

認証に失敗したとき — 削除されたキー、期限切れのトークン、失効したセッション — どちらのクライアントも ListenHubError を投げます:

import { OpenAPIClient, ListenHubError } from '@marswave/listenhub-sdk';

const client = new OpenAPIClient();

try {
  await client.getSubscription();
} catch (err) {
  if (err instanceof ListenHubError) {
    // err.status   → HTTP status (e.g. 401)
    // err.code     → backend error code from the envelope
    // err.requestId → include this when contacting support
    console.error(`[${err.status}] ${err.message} (request ${err.requestId})`);
  }
}

ListenHubErrorstatuscoderequestId を持ちます。401403 は認証情報が拒否されたことを意味します — API キーならローテーションし、OAuth トークンなら refresh を実行して、それも失敗する場合はユーザーをログインフローに戻してください。レート制限された(429)リクエストは Retry-After に従って最大 maxRetries 回(デフォルト 2)自動でリトライされるため、エラーとして表面化することはほとんどありません。

次のステップ

このページの内容