extension-core-infrastructure について
Caffeine AI 上でアプリを開発する人向けの基盤コンポーネントです。Internet Identity 認証とバックエンド Actor 管理を提供し、ID の変更に応じて Actor を再作成します。
主な機能
- 01Internet Identity 認証
- 02Google、Microsoft、ワークスペース SSO によるログイン
- 03ログイン状態とセッション復元の管理
- 04ID 変更時のバックエンド Actor 再作成
- 05ビジュアル QA 用モックバックエンド
活用シーン
- 01ID 状態に応じた画面アクセス制御
- 02Internet Identity の複数のログイン方法を追加
- 03ログインやログアウト後のバックエンド接続更新
- 04ビジュアル QA でモックバックエンドを利用
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ caffeinelabs/ skills --skill extension-core-infrastructureインストール後、Claude Code で/extension-core-infrastructureを入力して読み込まれたか確認します。
同梱ファイルmigration/v0.x.y-to-v1.x.y.mdmigration/v1.x.y-to-v2.x.y.md
詳細
コアインフラストラクチャ
Caffeine AI のコアインフラストラクチャ拡張機能です。
概要
このコンポーネントは、バックエンド接続の設定、Internet Identity 認証フック、アクター管理ユーティリティなど、すべてのプロジェクトの基盤となるインフラストラクチャを提供します。
要件
"@caffeineai/core-infrastructure": "^2.0.0"
"@caffeineai/object-storage": "^1.1.0"
"@icp-sdk/auth": "^9.0.0"
"@icp-sdk/core": "^5.3.0"
@caffeineai/object-storage は core-infrastructure のピア依存関係です。各プロジェクトで直接 npm 依存関係としてインストールする必要があります(ビルドテンプレートには両方のパッケージが含まれています)。
統合
コアインフラストラクチャはすべてのプロジェクトに自動的に含まれます。手動での統合手順は必要ありません。
フロントエンド
コアインフラストラクチャのフロントエンドパッケージ(@caffeineai/core-infrastructure)は、すべてのプロジェクトに自動的に含まれます。
アプリのエントリーポイント
InternetIdentityProvider と QueryClientProvider でアプリをラップします。
import { InternetIdentityProvider } from "@caffeineai/core-infrastructure";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import ReactDOM from "react-dom/client";
import App from "./App";
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById("root")!).render(
<QueryClientProvider client={queryClient}>
<InternetIdentityProvider>
<App />
</InternetIdentityProvider>
</QueryClientProvider>,
);
useInternetIdentity() — 認証フック
Internet Identity の ID 状態、ログイン、ログアウトを提供します。
戻り値
| フィールド | 型 | 説明 |
|---|---|---|
identity | Identity | undefined | ユーザーの ID(ログイン後またはセッション復元後に利用可能) |
login | (options?: LoginOptions) => void | II のポップアップを開きます。完了を待たずに実行されます — await は使用しないでください。ログイン方法を参照してください。 |
clear | () => void | ログアウトし、保存済みの ID を消去します。完了を待たずに実行されます。 |
isAuthenticated | boolean | 有効な ID がある場合は true です。UI の表示制御にはこれを使用してください。 |
isInitializing | boolean | AuthClient が IndexedDB から読み込まれている間は true です |
isLoggingIn | boolean | II のポップアップが開いている間は true です |
isLoginSuccess | boolean | インタラクティブなログイン後にのみ true です(ページ再読み込み後のセッション復元ではありません) |
isLoginError | boolean | ログインまたは初期化に失敗した場合は true です |
isSessionExpired | boolean | セッションが終了した場合(アイドル状態/TTL/プロバイダーによる取り消し)に true です。意図的なログアウトではありません |
loginError | Error | undefined | isLoginError が true の場合のエラーオブジェクトです |
認証状態のライフサイクル
| シナリオ | loginStatus | isAuthenticated |
|---|---|---|
| ページ読み込み時に保存済みセッションがない | "idle" | false |
| 保存済みセッションを復元中 | "initializing" | false → true |
| 再読み込み後に保存済みセッションを復元 | "idle" | true |
| インタラクティブなログイン中 | "logging-in" | false |
| インタラクティブなログインが完了した直後 | "success" | true |
| ログインポップアップに失敗/キャンセル | "loginError" | false |
| セッションの有効期限切れ(アイドル状態/TTL/取り消し) | "expired" | false |
| 別のタブでログアウト | "idle" | false |
重要: isLoginSuccess が true になるのは、ポップアップを介したインタラクティブなログインの後のみです。ページ再読み込み時に保存済みの ID が復元された場合は該当しません。条件付きレンダリングには常に isAuthenticated を使用してください。
認証状態はクライアントインスタンス上ではなく保存されます。あるタブでログインまたはログアウトすると、ほかのタブにも反映されます。セッションの有効期限が切れた状態(isSessionExpired)は意図的なログアウトではありません。ログアウト後のランディングページではなく、セッション終了画面を表示してください。
使用方法
isAuthenticated に基づいて、認証が必要な UI の表示を制御します。
const { isAuthenticated } = useInternetIdentity();
{isAuthenticated ? <AuthenticatedApp /> : <LoginScreen />}
初期化中またはログイン中は、ログインボタンを無効にします。
const { login, isInitializing, isLoggingIn } = useInternetIdentity();
<button onClick={() => login()} disabled={isInitializing || isLoggingIn}>
Sign in
</button>
login() と clear() は完了を待たずに実行されます — 非同期処理のライフサイクルはフックの状態フィールド(isLoggingIn、isInitializing)で追跡してください。ローカルな useState や isPending のロジックでラップしないでください。
サインインの種類:標準の II、Google、Microsoft、ワークスペース SSO
login() は、ユーザーのサインイン方法を選択する省略可能な LoginOptions オブジェクトを受け取ります。どの種類も Internet Identity を経由し、同じアイデンティティ、セッション動作、ログアウト処理を使用します。最初に表示される画面だけが異なります。
login(); // Plain Internet Identity sign-in
login({ provider: "google" }); // One-click Google sign-in (II opens Google OAuth directly)
login({ provider: "microsoft" }); // One-click Microsoft sign-in (II opens Microsoft OAuth directly)
login({ ssoDomain: "acme.com" }); // Company/workspace SSO via the domain's identity provider
- Google:Google API キーや OAuth クライアントの設定は不要です。OAuth フローは Internet Identity が処理します。
- Microsoft:Azure/Entra アプリの登録は不要です。OAuth クライアントは Internet Identity が管理します。個人用 Microsoft アカウントと職場または学校アカウントの両方を使用できます。
- ワークスペース SSO:ユーザーが会社のドメイン(例:
acme.com)を入力すると、Internet Identity がhttps://<domain>/.well-known/ii-openid-configurationから会社の OpenID Connect プロバイダーを検出し、そのプロバイダーでサインインします(Okta、Entra ID、および会社が設定したその他の OIDC プロバイダーで動作します)。特定の会社独自のテナントが必要なアプリではこちらを使用してください。会社ごとの設定が不要なワンクリックの Microsoft ボタンには、provider: "microsoft"を使用してください。 - Apple サインインは提供されていません。Internet Identity は Apple のメールアドレスや名前のクレームを返さないため、属性コールバックが空になるからです。
- すべての種類のセッションは同じ方法で保存されます。
isAuthenticated、再読み込み時のセッション復元、clear()の動作は、使用した種類にかかわらず同じです。 - バックエンドが
caffeineai-authorizationを使用している場合、Google、Microsoft、SSO でのサインインでは、検証済みの名前/メールアドレス属性(および SSO ドメイン)が属性コールバックに自動的に渡されます。extension-authorizationスキルを参照してください。
login() はボタンの onClick ハンドラーからのみ呼び出してください。 Internet Identity のポップアップは、実際のクリックイベントが処理中の場合にのみ開けます。それ以外の場合は Signer window should not be opened outside of click handler というエラーで失敗します。特に、次の点に注意してください。
login()をフォームのonSubmitから呼び出さないでください。submitイベントはクリックイベントの終了後に発生するため、チェックに失敗します。SSO ドメインの入力には、通常の<div>(<form>ではなく)とtype="button"の送信ボタンを使用し、そのonClickでドメインを検証してlogin({ ssoDomain })を直接呼び出してください。login()をキーボードハンドラー(ドメイン入力での Enter など)やawaitの後から呼び出さないでください。どちらもクリックイベントの処理中には実行されません。
アプリで login の呼び出し前に SSO ドメインを検証する場合は、Internet Identity 独自のルールに合わせてください。2 つ以上のラベルを持つ通常の DNS 名(例:acme.com)、またはループバックホスト(localhost または 127.0.0.1。任意で :port を指定可能。例:localhost:3000)を受け付けます。II はローカルテスト用のループバックドメインを受け付けるため、入力を拒否しないようにしてください。
標準的なサインイン UI のパターンは、目立つ Google ボタンと Microsoft ボタン、標準の II サインイン、ドメインを入力する「会社の SSO」オプションです。
function SignInOptions() {
const { login, isInitializing, isLoggingIn } = useInternetIdentity();
const [ssoDomain, setSsoDomain] = useState("");
const disabled = isInitializing || isLoggingIn;
return (
<div>
<button onClick={() => login({ provider: "google" })} disabled={disabled}>
Continue with Google
</button>
<button onClick={() => login({ provider: "microsoft" })} disabled={disabled}>
Continue with Microsoft
</button>
<button onClick={() => login()} disabled={disabled}>
Sign in with Internet Identity
</button>
{/* Company SSO: deliberately not a <form> — login must run inside the
button's click event, and form onSubmit fires after the click ends */}
<input
value={ssoDomain}
onChange={(e) => setSsoDomain(e.target.value)}
placeholder="yourcompany.com"
/>
<button
onClick={() => login({ ssoDomain: ssoDomain.trim() })}
disabled={disabled || !ssoDomain.trim()}
>
Sign in with your company
</button>
</div>
);
}
アプリで実際に必要な種類だけを提供してください。Google、Microsoft、または会社の SSO によるサインインが求められていない場合は、標準の login() を使用してください。いずれかが求められている場合、サインインページには、求められている直接サインインのオプション(Google ボタン、Microsoft ボタン、または SSO ドメイン入力)を表示し、さらにフォールバックとして標準の「Internet Identity でサインイン」ボタンも残してください。Google アカウント、Microsoft アカウント、または登録済みの会社ドメインを持たないユーザーもサインインできる必要があります。
useActor() — バックエンド Actor フック
型付きのバックエンド Actor インスタンスを作成して管理します。ユーザーのアイデンティティが変わると(ログイン/ログアウト)、Actor を自動的に再作成します。
import { useActor } from "@caffeineai/core-infrastructure";
import { createActor } from "@/backend";
function MyComponent() {
const { actor, isFetching } = useActor(createActor);
// actor is null while loading, then the typed backend actor
if (!actor || isFetching) return <Loading />;
// Call backend methods directly
const data = await actor.myBackendMethod();
}
戻り値
| フィールド | 型 | 説明 |
|---|---|---|
actor | T | null | 型付きのバックエンド Actor。読み込み中は null |
isFetching | boolean | Actor の作成中は true |
アイデンティティが変わると(ログイン、ログアウト、またはセッション復元)、新しいアイデンティティで Actor が自動的に再作成され、依存するすべてのクエリが無効化されて再取得されます。
ビジュアル QA 用のモックバックエンド(VITE_USE_MOCK=true)
useActor は、カナスターに接続する代わりに、アプリが所有するモックを使用できます。モックを src/frontend/src/mocks/backend.ts に配置して mockBackend をエクスポートし、アプリのソースコードから glob を渡します。
import { useActor } from "@caffeineai/core-infrastructure";
import { createActor } from "@/backend";
const mockModules = import.meta.glob("../mocks/backend.{ts,tsx,js,jsx}");
export function useAppActor() {
return useActor(createActor, { mockModules });
}
import.meta.glob は呼び出しを含むファイルを基準に Vite が解決するため、glob はアプリ内に記述する必要があります。パッケージからはアプリの mocks/ ディレクトリが見えません。glob を使うことで、モックファイルが存在しない場合もビルドが成功します。モックが使用されるのは VITE_USE_MOCK=true の場合のみです。それ以外の場合、useActor は従来どおり動作し、実際のバックエンド設定が読み込まれます。
React の外部では、createActorWithConfig(createActor, { mockModules }) が同じオプションを受け付けます。また、loadMockBackendFromModules(mockModules) はモックを独自に解決します。