コンテンツにスキップ

ストア & イベント

概要

@authrim/sveltekit は状態管理に2つの相補的メカニズムを使用します:

  • Svelteストア — UI描画のためのリアクティブな Readable ストア
  • イベント — 副作用やカスタムロジックのための型付きイベントエミッター

イベントが真のデータソースです。ストアはイベントから自動更新(投影)されます — ストアへの直接書き込みは行いません。

ストア

auth.stores でストアにアクセス:

<script lang="ts">
import { getAuthContext } from '@authrim/sveltekit';
const auth = getAuthContext();
const { session, user, isAuthenticated, loadingState, error } = auth.stores;
</script>
{#if $isAuthenticated}
<p>こんにちは、{$user?.displayName}</p>
{:else}
<p>サインインしていません</p>
{/if}

ストアリファレンス

ストア説明
sessionReadable<Session | null>現在のセッションオブジェクト
userReadable<User | null>現在のユーザーオブジェクト
isAuthenticatedReadable<boolean>session !== null から導出
loadingStateReadable<AuthLoadingState>現在のローディング状態
errorReadable<AuthError | null>最後の認証エラー

AuthLoadingState

type AuthLoadingState =
| 'idle' // 安定 — 処理なし
| 'initializing' // 初回セッション確認
| 'authenticating' // ログイン/サインアップ中
| 'refreshing' // セッション更新中
| 'signing_out'; // サインアウト中

'idle' が安定状態です。操作が完了(成功またはエラー)すると、常に 'idle' に戻ります。

ローディング状態の例

<script lang="ts">
import { getAuthContext } from '@authrim/sveltekit';
const auth = getAuthContext();
const { loadingState, error } = auth.stores;
</script>
{#if $loadingState === 'authenticating'}
<div class="overlay">サインイン中...</div>
{:else if $loadingState === 'signing_out'}
<div class="overlay">サインアウト中...</div>
{/if}
{#if $error}
<div class="alert" role="alert">
<p>{$error.message}</p>
<code>{$error.code}</code>
</div>
{/if}

ストアエラー型

interface AuthError {
code: string; // エラーコード(例:'AR003001')
message: string; // 人間可読なメッセージ
details?: unknown; // 追加メタデータ
}

イベント

auth.on() で認証イベントを購読。解除関数を返します。

const unsubscribe = auth.on('auth:login', (payload) => {
console.log('ログイン:', payload.user.displayName);
console.log('方式:', payload.method); // 'passkey' | 'emailCode' | 'social'
});
// クリーンアップ
unsubscribe();

イベントリファレンス

イベントペイロードトリガー
auth:login{ session, user, method }ユーザーがログインに成功
auth:logout{ redirectUri? }ユーザーがサインアウト
auth:error{ error: AuthError }認証エラー発生
session:changed{ session, user }セッションまたはユーザーデータが変更
session:expired{ reason }セッションが無効化
token:refreshed{ session }トークンの更新に成功

イベントペイロード型

interface AuthEventPayloads {
'auth:login': {
session: Session;
user: User;
method: 'passkey' | 'emailCode' | 'social';
};
'auth:logout': { redirectUri?: string };
'auth:error': { error: AuthError };
'session:changed': { session: Session | null; user: User | null };
'session:expired': { reason: 'timeout' | 'revoked' | 'logout' };
'token:refreshed': { session: Session };
}

コンポーネントでのイベント使用

<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { getAuthContext } from '@authrim/sveltekit';
import { goto } from '$app/navigation';
const auth = getAuthContext();
let unsubscribes: (() => void)[] = [];
onMount(() => {
unsubscribes.push(
auth.on('auth:login', () => {
goto('/dashboard');
}),
auth.on('session:expired', ({ reason }) => {
if (reason === 'revoked') {
goto('/login?reason=session-revoked');
}
}),
);
});
onDestroy(() => {
unsubscribes.forEach(fn => fn());
});
</script>

イベント → ストア投影

イベントは自動的にストアを更新します。手動での同期は不要です:

イベントストア更新
auth:loginsession ← payload.session, user ← payload.user, loadingState'idle', errornull
auth:logoutsessionnull, usernull, loadingState'idle', errornull
auth:errorerror ← payload.error, loadingState'idle'
session:changedsession ← payload.session, user ← payload.user

SSR同期

AuthProvider でSSRデータを使用する場合、初期セッションは同期的に(最初のレンダリング前に)設定され、ハイドレーションの不一致を防ぎます:

sequenceDiagram
    participant Server as サーバー (hooks)
    participant Layout as +layout.svelte
    participant Provider as AuthProvider
    participant Stores as Svelte Stores

    Server->>Layout: SSR認証データ
    Layout->>Provider: initialSession, initialUser
    Provider->>Stores: _syncFromSSR()(同期的)
    Note over Stores: session & user が
最初のレンダリング前に設定 Provider->>Provider: onMount: バックグラウンドで再検証

これにより $isAuthenticated が最初のレンダリングで正しい値を持ち、未認証コンテンツのフラッシュが発生しません。

次のステップ