コンテンツにスキップ

認証と API キー

DUST API は、DUST のアカウントサービスである AuthD が発行したベアラー JWT を使用して、すべての /api/v1/* リクエストを認証します。API インテグレーションは、個人としてではなく、組織が所有するマシンアイデンティティである サービスアカウントとして動作します。フローは次のとおりです。

  1. 組織管理者がサービスアカウントを作成し、その認証情報を発行します(1 回のみ)。
  2. インテグレーションが認証情報を有効期間の短いベアラートークンと交換します。
  3. API 呼び出しで Authorization: Bearer <token> を送信し、トークンの有効期限が切れたら再度交換します。

サービスアカウントは、独立したマシンアイデンティティです。必ず 1 つの組織に所属し、メンバーと同様にチームへのアクセス権を付与できます。また、実行したすべての操作は、設定を行った従業員ではなく、そのサービスアカウントによる操作として監査台帳に記録されます。サービスアカウントの認証情報は、個人アカウントに影響を与えることなく、いつでもローテーションまたは失効できます。

2 種類の認証情報を利用でき、1 つのサービスアカウントで両方を保持できます。

  • API キー — 最もシンプルなインテグレーションです。1 回の HTTP 呼び出しでキーをトークンと交換します。
  • OAuth2 クライアント(client_credentials) — OAuth2 を標準でサポートするエンタープライズミドルウェア(SAP Integration Suite、MuleSoft、Boomi など)向けです。

サービスアカウントとその認証情報は、組織管理者が authd.dustid.io の AuthD ポータルで管理します。

  1. 組織管理者として authd.dustid.io にサインインします。
  2. 組織ページを開き、サービスアカウントタブを選択します。
  3. サービスアカウントを作成します(例:「SAP Connector」や「Line 3 scanner station」)。
  4. サービスアカウントの管理を開き、API キーを作成します。
  5. キーをシークレットマネージャーに保存し、パスワードと同様に扱います。キーが表示されるのは一度だけです。

認証情報をサーバー側に保管する

Section titled “認証情報をサーバー側に保管する”

最初の例に進む前に、ここをお読みください。認証情報をどこに置くかという判断が、DUST インテグレーションのセキュリティを左右します。

  • 認証情報はサーバー上にのみ保管します — 環境変数またはシークレットマネージャーを使用し、クライアントバンドルやソース管理には決して含めないでください。
  • ベアラートークンも認証情報です。 有効期間は短いものの、認証情報から発行されたトークンは、そのサービスアカウントがアクセスできるすべての組織、チーム、操作を含む、サービスアカウントの完全なアクセス権で動作します。有効期間が短いことで制限されるのは悪用可能な時間であり、影響範囲ではありません。
  • Web アプリやモバイルアプリで DUST データが必要な場合、サポートされる構成は「ブラウザー → お客様のバックエンド → DUST API」です。 バックエンドが認証情報を保持してベアラートークンを発行し、呼び出し元に許可するコンテキストと操作を判断したうえで、DUST API 自体を呼び出します。ブラウザーが DUST の認証情報を受け取ることはありません。スキャナーおよびモバイルのインテグレーションもまったく同じ構成に従います。撮影データはお客様のバックエンドに送信され、そのバックエンドがサーバーに保管された認証情報を使用して識別子エンドポイントを呼び出します。
  • アプリケーションおよび環境ごとに 1 つのサービスアカウントを使用すると、ローテーション、失効、監査を対象に絞って実施できます。

キーをベアラートークンと交換する

Section titled “キーをベアラートークンと交換する”

GET /api/auth/token は x-api-key ヘッダーで API キーを受け取り、JWT を返します。(APID がこのリクエストを AuthD にプロキシするため、すべてに 1 つのベース URL を使用できます。)

この呼び出しと、その結果を使用するすべての呼び出しは、サーバー上で実行します。

Terminal window
curl -fsS "https://apid.dustid.io/api/auth/token" \
-H "x-api-key: $DUST_API_KEY"

レスポンス:

{ "token": "eyJhbGciOi...", "expiresIn": 900, "expiresAt": "2026-07-14T22:40:00.000Z" }

expiresIn はトークンの残りの有効期間を秒単位で示し、expiresAt は同じ時点を ISO 8601 タイムスタンプで示します。どちらもトークン自体の有効期限クレームから導出されるため、有効期限なしで発行されたトークンの場合、返されるのは { "token": "…" } のみです。これらの値は存在しない可能性を考慮して読み取り、その場合は独自の安全側の余裕時間を使用してください。次回の交換時期を決めるには、いずれかを使用してください。有効期間をハードコードしないでください。

OAuth2 をネイティブに使用するプラットフォームでは、API キーの代わりに、または API キーと併せて、サービスアカウントに OAuth クライアントを作成します。クライアント ID とシークレットが表示されるのは、作成時の一度だけです。

標準の client_credentials グラントを使用して、サービスアカウントのトークンエンドポイントからトークンをリクエストします。client_secret_post(フォームフィールド)と client_secret_basic(HTTP Basic)の両方に対応しています。

Terminal window
curl -fsS "https://authd.dustid.io/api/auth/dust/service-accounts/token" \
-d grant_type=client_credentials \
-d client_id="$DUST_CLIENT_ID" \
-d client_secret="$DUST_CLIENT_SECRET"

レスポンス(標準の OAuth2 トークンレスポンス):

{ "access_token": "eyJhbGciOi...", "token_type": "Bearer", "expires_in": 900 }

取得したトークンの形式と権限は、API キー交換で取得したものと同一です。同じ方法で使用してください。ミドルウェアで「token URL」の入力を求められた場合は、上記のエンドポイントを使用してください。

すべてのコア API 呼び出しでトークンを送信します。

Authorization: Bearer <token>

トークンが機能することを簡単に確認する方法は次のとおりです。

Terminal window
curl -fsS "https://apid.dustid.io/api/v1/me" \
-H "Authorization: Bearer $DUST_TOKEN"

有効なトークンがないリクエストには、本文 { "code": "UNAUTHORIZED", "message": "...", "status": 401 } とともに 401 が返されます。エラーの規約についてはリクエスト規約を、コードの全一覧についてはエラーとスキャン結果を参照してください。

サービスアカウントのベアラートークンは有効期間が短く、現在は 15 分です。ただし、この値をハードコードせず、必ずレスポンスから有効期間を読み取ってください(キー交換では expiresIn/expiresAt、OAuth グラントでは expires_in)。リフレッシュトークンはありません。トークンの有効期限が切れたら、認証情報を再度交換します。

堅牢なクライアントでは、両方のパターンを組み合わせます。安全側の余裕時間を設けて事前に更新し、1 回の 401 を更新および再試行のシグナルとして扱います(これにより、クロックスキューや有効期間中の失効にも対応できます)。

let cached: { token: string; refreshAfter: number } | null = null;
async function getToken(): Promise<string> {
if (cached && Date.now() < cached.refreshAfter) return cached.token;
const res = await fetch("https://apid.dustid.io/api/auth/token", {
headers: { "x-api-key": process.env.DUST_API_KEY! },
});
if (!res.ok) throw new Error(`token exchange failed: ${res.status}`);
const { token, expiresIn } = await res.json();
// refresh 60s before expiry, never cache a token for less than 5s
cached = { token, refreshAfter: Date.now() + Math.max(expiresIn - 60, 5) * 1000 };
return token;
}
async function apiFetch(url: string, init: RequestInit = {}): Promise<Response> {
const call = async () => {
// new Headers() handles every HeadersInit shape (plain object, Headers,
// tuple array) — an object spread would silently drop the latter two.
const headers = new Headers(init.headers);
headers.set("Authorization", `Bearer ${await getToken()}`);
return fetch(url, { ...init, headers });
};
let res = await call();
if (res.status === 401) {
cached = null; // token revoked or expired early — refresh once and retry
res = await call();
}
return res;
}

交換処理の負荷は小さいため、長期間保持するキャッシュを構築しないでください。有効期間の短さは、インシデント対応にも役立ちます。認証情報を失効させると新しいトークンの発行は即座に停止し、発行済みのトークンも数分以内に無効になります。

サービスアカウントはシステムを認証します。ERP や製造現場で、どの人物がボタンを押したのかを DUST に伝えることはできません。その追跡可能性が必要な場合は、小さな JSON オブジェクトである Dust-Ctx-Declared-Actor ヘッダーを使用して、リクエストごとに申告してください。

Dust-Ctx-Declared-Actor: {"id": "JDOE", "system": "SAP", "displayName": "Jane Doe"}
  • id は必須です。system、displayName、role は任意です。値は URI エンコードできます(非 ASCII 文字を含む場合は必須)。また、1 KB 未満に収める必要があります。
  • 申告された実行者は、そのリクエストが書き込むすべてのイベントにそのまま記録され、アクティビティ履歴には申告された帰属情報として表示されます。この情報はインテグレーションから提供されるものであり、DUST による検証は行われません。また、権限を付与または制限することもありません。
  • 組織管理者は、サービスアカウントの帰属情報ポリシーを必須に設定できます。この場合、申告された実行者を含まない書き込みリクエストは 403 ATTRIBUTION_REQUIRED で拒否されます。

API は、AuthD の JSON Web Key Set を使用して各ベアラートークンの署名を検証し、発行者(本番環境では https://authd.dustid.io/api/auth)と audience クレームを確認します。通常、この詳細を把握する必要はありません。ただし、独自のバックエンドで DUST が発行した JWT を検証する場合(たとえば、別の内部サービスから転送されたトークンを信頼する場合)、JWKS は公開されています。

GET https://apid.dustid.io/api/auth/jwks

任意の JOSE ライブラリーで使用できる、標準の { "keys": [ ... ] } 文書が返されます。

ブラウザースキャンインテグレーションの全体像

Section titled “ブラウザースキャンインテグレーションの全体像”

以下のパターンは、すべてのブラウザーまたはモバイルのスキャンインテグレーションが採用すべき構成です。2 つのファイル、2 つの実行場所、1 つの認証情報で構成され、その認証情報が 2 番目のファイルから外に出ることはありません。

scanner.tsx — runs in the BROWSER
// No DUST credential appears in this file, and none should.
async function identify(capture: Blob) {
const form = new FormData();
form.set("capture", capture);
// Your own endpoint, authenticated with your own session.
const response = await fetch("/api/identify", { method: "POST", body: form, credentials: "same-origin" });
return await response.json();
}
server/identify.ts — runs on YOUR SERVER
// Holds the DUST credential, mints the bearer token, chooses the context,
// and applies your own authorization before calling DUST.
export async function handleIdentify(request: Request, session: YourSession) {
if (!session.mayScan) return new Response("Forbidden", { status: 403 });
const capture = (await request.formData()).get("capture") as Blob;
const form = new FormData();
form.set("tagType", "DUST");
form.set("data", capture);
form.set("searchTeamIds", JSON.stringify(session.allowedTeamIds));
return await fetch("https://apid.dustid.io/api/v1/tags/identify", {
method: "POST",
headers: {
Authorization: `Bearer ${await getToken()}`, // server-held credential
"Dust-Ctx-Org-Id": session.organizationId, // your choice, not the caller's
},
body: form,
});
}

独自のプロキシは、DUST API が把握できないユーザーごとのルールを適用する自然な場所でもあります。たとえば、この従業員が検索できるチーム、識別だけでなくバインドも許可するかどうか、何をログに記録するかを制御できます。

1 つのサービスアカウントで複数の認証情報を同時に有効にできるため、ローテーションのためにダウンタイムを設ける必要はありません。

  1. 同じサービスアカウントに代替キー(または OAuth クライアント)を作成します。
  2. アプリケーションにデプロイします(重複期間中は両方の認証情報が機能します)。
  3. 本番トラフィックで新しい認証情報が使用されていることを確認します。各キーが最後に使用された時刻はポータルで確認できます。
  4. 古い認証情報を失効させます。
  • API クイックスタート — 5 分で、トークンの取得から最初のスレッドの作成までを行います。
  • リクエスト規約 — 組織を範囲とするすべての呼び出しに必要なコンテキストヘッダーについて説明します。
  • エラーとスキャン結果 — 401 の処理、401 発生時に一度だけ更新する方法、およびその他の失敗時の規約について説明します。
  • 完全な API リファレンス — すべてのエンドポイントとスキーマを掲載しています。