Frame.io TypeScript SDK — 認証ガイド

このガイドでは、Frame.io TypeScript SDKframeio)を使用してFrame.io APIで認証する方法について説明します。 Frame.io V4 APIはAdobe Identity Management Service(IMS)、アドビのOAuth 2.0アイデンティティプラットフォームを使用します。 これはTypeScript/JavaScript開発者向けのスタンドアロンリファレンスです。 以下のすべてのコード例と流量はframeioパッケージのみに対応しています。


TypeScript SDKの認証タイプ

TypeScript SDKは4つのOAuth認証クラスに加えて、直接トークン使用をサポートしています:

メソッド使用事例ユーザーとのやり取りが必要?クライアントシークレットが必要?
静的トークンクイックスクリプト、テスト、または既にトークンを持っている場合
サーバー間バックエンドサービス、cronジョブ、自動化はい
web アプリサーバーサイドアプリ(Express、Fastify、Next.js)はいはい
SPA(PKCE)シークレットを保存できないブラウザーアプリはい
ネイティブアプリ(PKCE)カスタム URI スキームリダイレクトを使用するデスクトップ/モバイルアプリはい
Server-to-Server では、ユーザーの操作なしでアプリをサービスアカウントとして動作させることができます。 これは、Adobe Admin Console で管理される Frame.io V4 アカウントでのみ利用できます。 Web アプリSPA では、アプリを特定のユーザーとして動作させることができます。 どちらも内部的に Adobe IMS を使用します。ユーザーがアプリを認証し、SDK が結果のコードをトークンと交換します。 TypeScript SDK が IMS /authorize/v2 および /token/v3 流量を処理します。 Web アプリにはクライアントシークレットが必要です。SPA では代わりに PKCE を使用します。 ネイティブアプリ は SPA と同じ PKCE 流量に従いますが、adobe+<hash>://callback の形式のリダイレクト URI を割り当てます</hash> リダイレクト URI を使用します。これは、アドビがネイティブアプリ資格情報に割り当てるものです。 これにより、認証後に OS レベルでアプリケーションがリダイレクトをインターセプトできます。

サービスアカウントユーザー

Server-to-Server認証を使用する場合、アプリケーションはサービスアカウントユーザーとして動作します。これは、サービスに代わってアクションを実行できる独特なアカウントタイプです。 これらはFrame.ioの他のユーザーに表示されます。サービスアカウントがアクションを実行すると、その名前がUIに表示されます。 Adobe Admin ConsoleDeveloper Consoleを通じてサービスアカウントアクセスを付与および取り消すことができます。 サービスアカウント名はFrame.io UIから管理されます。 最初に作成する S2S 接続は、デフォルトで Service Account User** と呼ばれ、2 番目は デフォルトで **Service Account User 2 と呼ばれます。

詳細については、Frame.io サーバー間サポートを使用した設定の自動化を参照してください。


クイックスタート

前提条件

  1. Adobe Developer Consoleからの資格情報
  • Client ID — すべてのOAuthフローに必要 - Client Secret — Server-to-ServerおよびWeb Appフローに必要 - Redirect URI — Web App、SPA、およびNative Appフローに必要。Adobeプロジェクトに登録する必要があります
  1. SDKをインストール:
$npm install frameio

方法の選択

  • ユーザーが関与しない場合 Server-to-ServerServerToServerAuth)を使用します。
  • ユーザーが関与し、シークレットを保存できる場合 Web AppWebAppAuth)を使用します。
  • ユーザーが関与するが、シークレットを保存できない場合 ブラウザーアプリにはSPASPAAuth)を、デスクトップ/モバイルアプリにはNative AppNativeAppAuth)を使用します。

アクセストークン

既にアクセストークンがある場合(他のOAuthシステムまたは以前の交換から、例えばAPI Explorer経由)、直接渡すことができます:

1import { FrameioClient } from "frameio";
2
3const client = new FrameioClient({ token: "YOUR_ACCESS_TOKEN" });

これは最もシンプルなアプローチですが、トークンは最終的に期限切れになり、SDKは自動的に更新しません。

レガシーデベロッパートークン

Adobe Admin Consoleでまだ管理されていないV4移行アカウントの場合、Frame.io developer siteからのレガシーデベロッパートークンを引き続き使用できます。 x-frameio-legacy-token-authヘッダーを含め、trueに設定する必要があります:

1import { FrameioClient } from "frameio";
2
3const client = new FrameioClient({
4 token: "YOUR_LEGACY_DEVELOPER_TOKEN",
5 headers: { "x-frameio-legacy-token-auth": "true" },
6});

レガシーデベロッパートークンは期限切れになりませんが、移行メカニズムです。 新しい統合と本番ワークロードには、以下のOAuth 2.0フローのいずれかを使用することをお勧めします。 詳細については、移行ガイドを参照してください。


Server-to-Server(クライアント資格情報)

ユーザーの操作なしにFrame.ioアクセスが必要なバックエンドサービスやスクリプトに使用します。 S2S 認証は、Adobe Admin Console で管理される Frame.io V4 アカウントでのみ利用できます。 アプリケーションは、人間のループなしでサービスアカウントユーザーとして認証されます。

1import { FrameioClient, ServerToServerAuth } from "frameio";
2
3const auth = new ServerToServerAuth({
4 clientId: "YOUR_CLIENT_ID",
5 clientSecret: "YOUR_CLIENT_SECRET",
6});
7
8const client = new FrameioClient({ token: () => auth.getToken() });

これで完了です。 auth.getToken()は、SDKがすべてのリクエストで呼び出す非同期関数です。 現在のトークンがまだ有効な場合は、すぐに戻ります。 期限切れが近い場合は、完全に透明に新しいトークンを最初に取得します。

仕組み

クライアント資格情報(クライアントID + シークレット)は期限切れになりません。 セキュリティ衛生のために手動でのみ回転させます。 S2Sは、手動介入なしで効果的に永続的で中断のないAPIアクセスを提供します。

内部的には:

  1. 最初のAPI呼び出しで、getToken()client_credentials付与を使用してAdobe IMSから新しいアクセストークンをリクエストします。
  2. トークンはメモリにキャッシュされます。 個人のアクセストークンは期限切れになりますが(通常24時間)、これは自動的に処理されます。
  3. キャッシュされたトークンが更新バッファー内にある場合(デフォルト:期限切れの60秒前)、SDKは同じクライアント資格情報を使用して自動的に新しいトークンを取得します。
  4. リフレッシュトークンは関与しません。 クライアント資格情報自体が長期間有効なシークレットであり、常に新しいアクセストークンを作成するために使用できます。

明示的な認証

トークンを積極的に取得したい場合(例えば、起動時に不正な資格情報で高速に失敗させるため):

1const auth = new ServerToServerAuth({ clientId: "...", clientSecret: "..." });
2await auth.authenticate(); // throws AuthenticationError if credentials are invalid
3const client = new FrameioClient({ token: () => auth.getToken() });

web アプリ(認証コード)

ユーザーがAdobe IDでログインするサーバーサイドアプリケーションに使用します。 このフローにはクライアントシークレットが必要で、サーバーに安全に保存する必要があります。

1

ユーザーをAdobe IMSにリダイレクトします

1 import { WebAppAuth } from "frameio";
2 import crypto from "crypto";
3
4 const auth = new WebAppAuth({
5 clientId: "YOUR_CLIENT_ID",
6 clientSecret: "YOUR_CLIENT_SECRET",
7 redirectUri: "https://yourapp.com/callback",
8 });
9
10 // Generate a cryptographically random state value to prevent CSRF attacks
11 const state = crypto.randomBytes(32).toString("hex");
12
13 const authorizationUrl = auth.getAuthorizationUrl({ state });
14 // Store `state` in the user's session, then redirect them to `authorizationUrl`
2

コールバックを処理

Adobe IMS がユーザーを redirectUri にリダイレクトした際、codestate パラメーターを抽出します。 state が保存したものと一致することを確認してから、code をトークンと交換します:

1 // In your callback handler (e.g. an Express route):
2 await auth.exchangeCode(req.query.code as string);

これにより認証コードをアクセストークンとリフレッシュトークンに交換し、両方を内部に保存します。

3

クライアントを使用

1 import { FrameioClient } from "frameio";
2
3 const client = new FrameioClient({ token: () => auth.getToken() });

これで完了です。 この時点から、getToken() がトークンのライフサイクルを自動的に管理します。 アクセストークンの有効期限が近づくと、SDK はリフレッシュトークンを使用して新しいトークンを取得します。 ユーザーの操作は不要です。

完全な Express の例

1import crypto from "crypto";
2import express from "express";
3import session from "express-session";
4import { FrameioClient, WebAppAuth } from "frameio";
5
6const auth = new WebAppAuth({
7 clientId: "YOUR_CLIENT_ID",
8 clientSecret: "YOUR_CLIENT_SECRET",
9 redirectUri: "http://localhost:3000/callback",
10});
11
12const app = express();
13app.use(session({ secret: crypto.randomBytes(32).toString("hex"), resave: false, saveUninitialized: false }));
14
15app.get("/login", (req, res) => {
16 const state = crypto.randomBytes(32).toString("hex");
17 (req.session as any).oauthState = state;
18 res.redirect(auth.getAuthorizationUrl({ state }));
19});
20
21app.get("/callback", async (req, res) => {
22 if (req.query.state !== (req.session as any).oauthState) {
23 return res.status(403).send("Invalid state parameter");
24 }
25
26 await auth.exchangeCode(req.query.code as string);
27
28 const client = new FrameioClient({ token: () => auth.getToken() });
29 const accounts = await client.accounts.index();
30 res.json(accounts);
31});
32
33app.listen(3000);

シングルページアプリケーション / PKCE(認証コード + PKCE)

ブラウザーベースのアプリケーション、デスクトップアプリケーション、またはクライアントシークレットを安全に保存できない CLI ツールに使用します。 この流量は PKCE (RFC 7636) を使用して認証コード交換を保護します。

1

認証 URL を生成

1 import { SPAAuth } from "frameio";
2
3 const auth = new SPAAuth({
4 clientId: "YOUR_CLIENT_ID",
5 redirectUri: "https://yourapp.com/callback",
6 });
7
8 const state = crypto.randomUUID();
9 const result = await auth.getAuthorizationUrl({ state });
10 // result.url -> redirect the user here
11 // result.codeVerifier -> store this securely until the callback

getAuthorizationUrl は完全な URL(PKCE code_challenge が埋め込まれた)と次のステップで必要な codeVerifier を含む AuthorizationUrlResult を返します。

2

検証機能付きでコードを交換

ユーザーがリダイレクトされた際:

1 await auth.exchangeCode({
2 code: "CODE_FROM_CALLBACK",
3 codeVerifier: result.codeVerifier,
4 });
3

クライアントを使用

1 import { FrameioClient } from "frameio";
2
3 const client = new FrameioClient({ token: () => auth.getToken() });

これで完了です。 リフレッシュは web アプリと同じように動作します — SDK がリフレッシュトークンを自動的に使用します。 違いは、SPA 流量がパブリッククライアント向けに設計されているため、リフレッシュ時にクライアントシークレットが送信されないことです。

codeVerifier は認証リクエストとコード交換の間、クライアント側で安全に保存する必要があります。 ブラウザーアプリでは sessionStorage または同等のものを使用します。


ネイティブアプリ(認証コード + PKCE)

デスクトップおよびモバイルアプリケーションに使用します。 Adobe Developer Console でネイティブアプリ資格情報を作成すると、アドビは adobe+<hash>://callback の形式のリダイレクト URI を割り当てます</hash> — OS レベルでそのカスタム URI スキームを処理するようにアプリケーションを登録します。 ループバックリダイレクト(http://127.0.0.1:<port>/callback</port>)もローカル開発でサポートされています。 フローは SPA と同じです — クライアントシークレットなしで PKCE を使用します。

1import { NativeAppAuth } from "frameio";
2
3const auth = new NativeAppAuth({
4 clientId: "YOUR_CLIENT_ID",
5 redirectUri: "adobe+abc123def456://callback", // from your Adobe Developer Console Native App credential
6 // Also supports loopback: "http://127.0.0.1:8080/callback"
7});
8
9const { url, codeVerifier } = await auth.getAuthorizationUrl({
10 state: crypto.randomUUID(),
11});
12
13// Open system browser to `url`
14// Listen for redirect on your custom URI scheme or loopback server
15
16await auth.exchangeCode({ code: "CODE_FROM_REDIRECT", codeVerifier });
17const client = new FrameioClient({ token: () => auth.getToken() });

リダイレクト URI ルール

アドビは 2 つのポイントでリダイレクト URI ルールを適用します:Adobe Developer Console で資格情報を登録するときと、redirect_uri パラメーターが /authorize/v2 エンドポイントにヒットするときです。 この SDK で redirectUri に渡す値は、資格情報に登録した「リダイレクト URI パターン」のいずれかと一致する必要があります — そうでなければ、アドビは代わりに資格情報のデフォルトリダイレクト URI にリダイレクトします。

  • Web AppSPA 資格情報には HTTPS が必要です。
  • Native App 資格情報は非 HTTPS リダイレクトを使用します — 通常は adobe+<hash>://callback</hash> URI で、資格情報の Developer Console に表示されます。

資格情報で受け入れられる正確なパターンについては、Adobe Developer Console を参照してください。

Python SDK には Native App 資格情報クラスは含まれていません。Python にはカスタム

URI スキームハンドラーを登録する標準的な方法がないためです。 TypeScript SDK は Native App を含むすべての 4 つの資格情報タイプをサポートしています。


手動トークン更新

Web App、SPA、および Native App フローでは、SDK は getToken() を介してトークンを自動的に更新します。 明示的な制御が必要な場合は、refresh() を直接呼び出すことができます:

1await auth.refresh(); // fetches a new access token using the refresh token

これは、自動更新バッファーに依存するのではなく、重要な操作の前に強制的に更新したい場合に便利です。

refresh()WebAppAuthSPAAuth、および NativeAppAuth で利用できます。 更新トークンが利用できない場合(つまり、最初に exchangeCode() を呼び出す必要がある場合)、ConfigurationError をスローします。 ServerToServerAuth には refresh() メソッドがありません — 代わりにクライアント資格情報を介して新しいトークンを取得するために authenticate() を使用します。


トークンの永続化

すべての認証クラスは、再起動間でトークン状態を永続化するために exportTokens()importTokens() をサポートしています。 web アプリ、SPA、ネイティブアプリの流量では、アクセストークンとリフレッシュトークンがデフォルトでメモリに保存されるため、これは特に重要です。アプリケーションが再起動すると、トークンを永続化しない限り、ユーザーは再認証する必要があります。 サーバー間では、永続化はオプションです(クライアント資格情報は常に新しいトークンを作成できます)が、キャッシュされたトークンを読み込むことで、起動時の余分なラウンドトリップを回避できます。

マーカーの読み込みと書き出し

1// After exchangeCode(), save the token state
2const tokenData = auth.exportTokens();
3// tokenData is: { access_token: "...", refresh_token: "...", expires_at: 1234567890.0 }
4// Save it to your database, file, or secret store
5
6// On next startup, restore it
7auth.importTokens(tokenData);
8const client = new FrameioClient({ token: () => auth.getToken() });
9// The SDK will automatically refresh if the token is near expiry

書き出したトークンは安全に保存してください。 これらにはAPIアクセスを付与するアクセストークンとリフレッシュトークンが含まれています。 トークンの書き込みは避けてください

本番環境でプレーンテキストファイルに保存することは避けてください。

onTokenRefreshedによる自動永続化

トークンが更新されるたびに自動的に保持するには、onTokenRefreshed コールバックを使用します:

1import fs from "fs/promises";
2
3const TOKEN_FILE = "tokens.json";
4
5const auth = new WebAppAuth({
6 clientId: "...",
7 clientSecret: "...",
8 redirectUri: "...",
9 onTokenRefreshed: (tokens) => {
10 fs.writeFile(TOKEN_FILE, JSON.stringify(tokens));
11 },
12});
13
14// On startup, restore if available
15try {
16 const saved = JSON.parse(await fs.readFile(TOKEN_FILE, "utf-8"));
17 auth.importTokens(saved);
18} catch {
19 // No saved tokens — user will need to authenticate
20}

コールバックは exportTokens() と同じ形式を受け取り、トークンの更新が成功するたびに実行されます。


トークンの取り消し

ユーザーをログアウトし、Adobe IMS でトークンを無効にするには:

1await auth.revoke();

これにより、Adobe IMS に対して 2 つのベストエフォート取り消しリクエストが並行して実行されます(アクセストークン用とリフレッシュトークン用)。そして、すべてのローカルトークン状態がクリアされます。機密クライアント(WebAppAuth)の場合、取り消しリクエストは HTTP Basic Auth を使用します。パブリッククライアント(SPAAuthNativeAppAuth)の場合、client_id はクエリパラメータとして送信されます。 取り消しエラーはログに記録されますが、スローされません。 取り消し後、ユーザーは再認証する必要があります。


エラーハンドリング

すべての認証エラーは FrameioAuthError を継承するため、広範囲でキャッチするか特定のケースを処理できます:

1import {
2 FrameioAuthError,
3 AuthenticationError,
4 TokenExpiredError,
5 ConfigurationError,
6 NetworkError,
7 RateLimitError,
8} from "frameio";
9
10try {
11 await auth.exchangeCode("...");
12} catch (error) {
13 if (error instanceof TokenExpiredError) {
14 // The refresh token has expired; redirect the user to sign in again
15 } else if (error instanceof AuthenticationError) {
16 // Token exchange failed
17 console.error(`Error: ${error.errorCode} - ${error.errorDescription}`);
18 } else if (error instanceof NetworkError) {
19 // Timeout or connection failure (after retries)
20 } else if (error instanceof RateLimitError) {
21 // 429 from Adobe IMS; retry after error.retryAfter seconds
22 } else if (error instanceof FrameioAuthError) {
23 // Catch-all for any other auth error
24 }
25}

エラーリファレンス

例外発生するタイミング
ConfigurationError設定が見つからないか無効(例:空の clientId、非 HTTPS リダイレクト URI、非 HTTPS imsBaseUrl
AuthenticationErrorAdobe IMS によってトークン交換または更新が却下されました(.errorCode.errorDescription があります)
TokenExpiredErrorリフレッシュトークン自体が期限切れ。ユーザーは再認証が必要
NetworkErrorすべての再試行後の HTTP タイムアウトまたは接続失敗
RateLimitErrorAdobe IMS が 429 を返しました。バックオフガイダンスについては .retryAfter を確認してください
PKCEErrorPKCE フローでの消費者使用に利用可能。SDK 内部では投げられない

本番環境での期限切れリフレッシュトークンの処理

web アプリ、SPA、およびネイティブアプリフローの場合、リフレッシュトークンは最終的に期限切れになります。その場合、getToken()TokenExpiredError を発生させます。 これをキャッチして、ユーザーを再度認証フローにリダイレクトする必要があります。

1import { TokenExpiredError } from "frameio";
2
3try {
4 const client = new FrameioClient({ token: () => auth.getToken() });
5 const assets = await client.files.list({ projectId: "..." });
6} catch (error) {
7 if (error instanceof TokenExpiredError) {
8 // Clear persisted tokens and redirect user to login
9 await auth.revoke();
10 return res.redirect("/login");
11 }
12}

設定リファレンス

これらのパラメーターには適切なデフォルト値があり、設定が必要になることはほとんどありません。 動作をカスタマイズする必要がある場合(ステージング IMSを指定する、カスタムfetchを挿入する、タイムアウトを調整する、ロガーを接続するなど)は、認証クラスを構築する際にオプションパラメーターとして渡してください:

パラメーターDefault説明
scopesフロー固有のデフォルトスペース区切りの OAuth 範囲。 S2S のデフォルトは openid AdobeID frame.s2s.all です。ユーザー向けフローのデフォルトは openid email profile offline_access additional_info.roles です。
imsBaseUrlhttps://ims-na1.adobelogin.comAdobe IMS ベース URL。 ステージングまたは本番環境以外の環境での上書き。 HTTPS を使用する必要があります。
fetchglobalThis.fetchプロキシ、mTLS、またはカスタム HTTP 処理用のカスタム fetch 実装。
timeout30000トークンエンドポイント呼び出しの HTTP リクエストタイムアウト(ミリ秒)。
maxRetries2一時的な障害(5xx、タイムアウト)の最大再試行回数。 レート制限の再試行(429)は別途追跡されます。
refreshBuffer60トークンの有効期限前に事前更新をトリガーする秒数。
onTokenRefreshedundefinedトークンの更新が成功するたびに実行されるコールバック。 access_tokenrefresh_token、およびexpires_atを含むオブジェクトを受け取ります。
logger何もしない(サイレント)debuginfowarnerrorメソッドを持つLoggerインスタンス(例:consolepinowinston)。

ステージング環境

imsBaseUrlを上書きしてステージングのAdobe IMSインスタンスを指定します。 SDKは、プログラムで本番環境の値を参照する必要がある場合に備えて、DEFAULT_IMS_BASE_URLhttps://ims-na1.adobelogin.com)も書き出します。

1const auth = new ServerToServerAuth({
2 clientId: "...",
3 clientSecret: "...",
4 imsBaseUrl: "https://ims-na1-stg1.adobelogin.com",
5});

カスタムfetch

プロキシサポートやカスタムTLS設定の場合:

1import { ProxyAgent } from "undici";
2
3const proxyAgent = new ProxyAgent("http://corporate-proxy:8080");
4
5const auth = new ServerToServerAuth({
6 clientId: "...",
7 clientSecret: "...",
8 fetch: (url, init) => fetch(url, { ...init, dispatcher: proxyAgent }),
9});

同時実行の安全性

TypeScript SDK は同時使用に対して安全です。 複数の getToken() 呼び出しが同時に発生し、更新が必要な場合、更新リクエストは 1 つだけ実行されます。 他の呼び出しは同じプロミスを待機し、同じ結果を受け取ります。 外部ロックは必要ありません。 この重複の除外は JavaScript のシングルスレッドイベントループと共有 Promise を使用します — 更新が既に実行中の場合、同時呼び出し元は 2 番目のリクエストを開始する代わりにそれに参加します。 リフレッシュ中にrevoke()が呼び出された場合、リフレッシュはAuthenticationErrorで却下され、トークンはクリアされたままになります。取り消しが常に優先されます。