# Session 管理 (SessionProvider)

`SessionProvider` 是 `@arcblock/did-connect-react` 函式庫的基石。它是一個 React Context Provider，封裝了所有與 session 相關的邏輯、狀態和操作。透過用 `SessionProvider` 包裹您的應用程式，您可以將使用者的 session 資訊和身份驗證方法提供給應用程式樹中的任何元件，而無需手動向下傳遞 props。

該元件處理從儲存 session token 和管理使用者資料到協調登入/登出流程以及自動刷新 session 的所有事務。

## 運作原理

`SessionProvider` 建立一個 session context，其中包含目前使用者的狀態（`user` 物件、`loading` 狀態等），並提供修改該狀態的函式（例如，`login`、`logout`）。任何巢狀在 `SessionProvider` 內的元件都可以訂閱此 context 以存取這些資料並觸發身份驗證操作。

這是一個說明基本架構的圖表：

<!-- DIAGRAM_IMAGE_START:architecture:4:3:1765962229 -->
![Session Management (SessionProvider)](assets/diagram/core-components-session-provider-01.zh-TW.jpg)
<!-- DIAGRAM_IMAGE_END -->

## 入門指南

要使用 session 管理功能，您首先需要匯入 `SessionProvider` 並用它包裹您的應用程式。該函式庫匯出工廠函式以建立一個已配置的 provider。

### 基本設定

設定 provider 最常見的方法是使用 `createAuthServiceSessionContext`，它已為在 ArcBlock 生態系統（Blocklets）內執行的應用程式預先配置。

```javascript App.js icon=logos:javascript
import React from 'react';
import { createAuthServiceSessionContext } from '@arcblock/did-connect-react/lib/Session';
import AuthButton from './AuthButton';

// 建立 SessionProvider 和一個用於存取 context 的 hook
const { SessionProvider, SessionContext } = createAuthServiceSessionContext();

export const useSession = () => React.useContext(SessionContext);

export default function App() {
  return (
    <SessionProvider>
      <div className="App">
        <h1>歡迎使用 DID Connect</h1>
        <AuthButton />
      </div>
    </SessionProvider>
  );
}
```

```javascript AuthButton.js icon=logos:javascript
import React from 'react';
import { useSession } from './App';

export default function AuthButton() {
  const { session } = useSession();

  if (session.loading) {
    return <div>載入中...</div>;
  }

  if (session.user) {
    return (
      <div>
        <p>歡迎，{session.user.fullName || session.user.did}</p>
        <button onClick={() => session.logout()}>登出</button>
      </div>
    );
  }

  return <button onClick={() => session.login()}>登入</button>;
}
```

在此範例中，`createAuthServiceSessionContext` 產生一個 `SessionProvider`。我們用它包裹 `App`，現在任何子元件，例如 `AuthButton`，都可以使用 `useSession` hook 來存取 session 資料和函式。

## SessionProvider Props

您可以透過傳遞各種 props 來設定 `SessionProvider` 的行為。

<x-field-group>
  <x-field data-name="serviceHost" data-type="string" data-desc="驗證服務後端的基礎 URL。預設為目前 Blocklet 的前綴或 '/'。"></x-field>
  <x-field data-name="autoConnect" data-type="boolean" data-default="false" data-desc="若為 true，如果在頁面載入時未找到使用者 session，DID Connect 對話方塊將自動開啟。"></x-field>
  <x-field data-name="autoDisconnect" data-type="boolean" data-default="true" data-desc="若為 true，如果應用程式 ID 與 session cookie 中儲存的 ID 不符，session 將被自動清除。"></x-field>
  <x-field data-name="protectedRoutes" data-type="string[]" data-default='["*"]' data-desc="一個用於保護路由模式的陣列。如果未經身份驗證的使用者嘗試存取這些路由，他們將看到一個載入指示器，直到 session 解析完成。支援萬用字元（*）。"></x-field>
  <x-field data-name="locale" data-type="string" data-desc="設定 DID Connect UI 的顯示語言。預設為瀏覽器的語言。"></x-field>
  <x-field data-name="webWalletUrl" data-type="string" data-desc="用於連接過程的網頁版 DID Wallet 的 URL。"></x-field>
  <x-field data-name="timeout" data-type="number" data-default="120000" data-desc="在連接過程中等待 DID Wallet 回應的逾時時間（毫秒）。"></x-field>
  <x-field data-name="useSocket" data-type="boolean" data-default="true" data-desc="決定是否使用 WebSocket 與錢包進行即時通訊，以提供更快的用戶體驗。"></x-field>
  <x-field data-name="extraParams" data-type="object" data-desc="一個包含額外參數的物件，這些參數將隨每次驗證請求發送到後端。"></x-field>
</x-field-group>

## Session Context 物件

當您使用 `SessionContext` 時，您會得到一個包含 session 狀態和 API 的物件。主要屬性是 `session`，它包含您將與之互動的所有資料和方法。

### 狀態屬性

這些是唯讀屬性，描述了使用者 session 的目前狀態。

<x-field-group>
  <x-field data-name="user" data-type="User | null" data-desc="如果 session 存在，則為經過身份驗證的使用者物件，否則為 null。包含 DID、姓名、電子郵件、頭像等詳細資訊。"></x-field>
  <x-field data-name="loading" data-type="boolean" data-desc="當 session 正在初始化、刷新或在登入/登出過程中為 true。"></x-field>
  <x-field data-name="initialized" data-type="boolean" data-desc="頁面載入時，初始 session 檢查完成後為 true。"></x-field>
  <x-field data-name="open" data-type="boolean" data-desc="如果 DID Connect 對話方塊目前處於開啟狀態，則為 true。"></x-field>
  <x-field data-name="error" data-type="string" data-desc="如果上一次 session 操作失敗，則包含錯誤訊息。"></x-field>
  <x-field data-name="provider" data-type="string" data-desc="上次登入使用的提供者（例如 'wallet'、'github'、'google'）。"></x-field>
  <x-field data-name="walletOS" data-type="string" data-desc="已連接的 DID Wallet 的作業系統（例如 'ios'、'android'、'web'）。"></x-field>
  <x-field data-name="unReadCount" data-type="number" data-desc="目前使用者的未讀通知數量。"></x-field>
</x-field-group>

### 操作方法

這些是您可以呼叫以啟動身份驗證流程或管理 session 的函式。

<x-field-group>
  <x-field data-name="login()" data-type="function">
    <x-field-desc markdown>透過開啟 DID Connect UI 來啟動使用者登入流程。它接受可選的回呼函式和參數。</x-field-desc>
  </x-field>
  <x-field data-name="logout()" data-type="function">
    <x-field-desc markdown>將目前使用者登出，並從儲存空間中清除 session。它接受一個可選的回呼函式，在完成後執行。</x-field-desc>
  </x-field>
  <x-field data-name="switchDid()" data-type="function">
    <x-field-desc markdown>允許已登入的使用者切換到不同的 DID 帳戶。它會重新開啟 DID Connect UI，並提供切換帳戶的上下文。</x-field-desc>
  </x-field>
  <x-field data-name="bindWallet()" data-type="function">
    <x-field-desc markdown>對於透過社群提供者（OAuth）或 Passkeys 登入的使用者，此函式會啟動一個流程，將 DID Wallet 連接到他們現有的帳戶。</x-field-desc>
  </x-field>
  <x-field data-name="refresh()" data-type="function">
    <x-field-desc markdown>手動觸發從伺服器刷新使用者 session 資料。適用於獲取更新的個人資料資訊或權限。</x-field-desc>
  </x-field>
  <x-field data-name="switchProfile()" data-type="function">
    <x-field-desc markdown>開啟 DID Connect UI，允許使用者在同一個 DID 帳戶內切換不同的個人資料（例如，個人、工作）。</x-field-desc>
  </x-field>
  <x-field data-name="switchPassport()" data-type="function">
    <x-field-desc markdown>開啟 DID Connect UI，允許使用者在不同的通行證之間切換，這些通行證可能代表不同的憑證集或角色。</x-field-desc>
  </x-field>
  <x-field data-name="connectToDidSpaceForFullAccess()" data-type="function">
    <x-field-desc markdown>啟動與使用者 DID Space 的連接，請求完全存取權限。這通常是需要讀取和寫入使用者個人資料儲存庫的應用程式所必需的。</x-field-desc>
  </x-field>
  <x-field data-name="withSecondaryAuth()" data-type="function">
    <x-field-desc markdown>一個高階函式，它包裹另一個函式，要求使用者在執行被包裹的函式之前完成二次身份驗證步驟（例如，重新輸入密碼、使用 passkey）。這是保護敏感操作的理想選擇。</x-field-desc>
  </x-field>
</x-field-group>

### 事件訂閱

context 還提供一個 `events` 物件，它是 `EventEmitter3` 的一個實例。您可以使用它來訂閱 session 的各種生命週期事件。

```javascript EventListener.js icon=logos:javascript
import React, { useEffect } from 'react';
import { useSession } from './App';

function EventListener() {
  const { events } = useSession();

  useEffect(() => {
    const handleLogin = (result) => {
      console.log('使用者已登入：', result.user.did);
    };

    const handleLogout = () => {
      console.log('使用者已登出');
    };

    events.on('login', handleLogin);
    events.on('logout', handleLogout);

    // 在元件卸載時清除監聽器
    return () => {
      events.off('login', handleLogin);
      events.off('logout', handleLogout);
    };
  }, [events]);

  return null; // 此元件不渲染任何內容
}
```

可用的事件包括 `login`、`login-failed`、`logout`、`change`、`bind-wallet`、`switch-passport` 等。

## 進階自訂

對於需要不同儲存機制或更精細控制的場景，您可以使用通用的 `createSessionContext` 工廠函式。

```javascript customSession.js icon=logos:javascript
import { createSessionContext } from '@arcblock/did-connect-react/lib/Session';

const { SessionProvider, SessionContext } = createSessionContext(
  'my_app_session_token', // 自訂儲存金鑰
  'ls',                   // 使用 localStorage 而非 cookie
  {},
  {
    rolling: false,         // 停用自動 token 刷新
  }
);

// ... 根據需要匯出和使用
```

這允許您微調 session 行為，例如將儲存引擎更改為 `localStorage`（'ls'）或 `cookie`，或調整 token 刷新策略。