# 身份验证方法

`DID Wallet` 是主要且最安全的身份验证方法，但 `@arcblock/did-connect-react` 也为多种替代登录方法提供了强大的支持，以提供灵活性并满足不同用户的偏好。本节详细介绍如何将 OAuth（社交登录）、Passkeys（无密码身份验证）和统一登录集成到您的应用程序中。

这些方法通过特定的 React Context Provider 启用，并通过相应的 Hook 进行访问，它们应嵌套在主 `SessionProvider` 内部。

---

## OAuth（社交登录）

集成 Google、GitHub 或 Apple 等流行的社交登录提供商，允许用户使用其现有帐户登录。此功能由 `OAuthProvider` 和 `useOAuth` Hook 管理。

### 1. 使用 OAuthProvider 进行设置

要启用 OAuth 功能，请使用 `OAuthProvider` 包裹您的组件树。通常将其放置在 `SessionProvider` 内部。

```jsx OAuthProvider Setup icon=logos:react
import { SessionProvider } from '@arcblock/did-connect-react';
import { OAuthProvider } from '@arcblock/did-connect-react/lib/OAuth';

function App() {
  return (
    <SessionProvider>
      <OAuthProvider
        onBindOAuth={(provider) => console.log(`${provider.provider} bound!`)}
        onUnbindOAuth={(account) => console.log(`${account.provider} unbound!`)}>
        {/* Your application components */}
      </OAuthProvider>
    </SessionProvider>
  );
}
```

**OAuthProvider 的 Props**

<x-field-group>
  <x-field data-name="children" data-type="ReactNode" data-required="true" data-desc="将有权访问 OAuth 上下文的子组件。"></x-field>
  <x-field data-name="locale" data-type="string" data-default="en" data-desc="用于 UI 消息和提示的语言。"></x-field>
  <x-field data-name="onBindOAuth" data-type="function" data-desc="用户成功绑定 OAuth 帐户后触发的回调函数。"></x-field>
  <x-field data-name="onUnbindOAuth" data-type="function" data-desc="用户成功解绑 OAuth 帐户后触发的回调函数。"></x-field>
  <x-field data-name="onSwitchPassport" data-type="function" data-desc="用户切换通行证后触发的回调函数。"></x-field>
  <x-field data-name="session" data-type="object" data-desc="当前用户会话对象，切换通行证 UI 时需要。"></x-field>
</x-field-group>

### 2. 使用 useOAuth Hook

`useOAuth` Hook 提供了与 OAuth 身份验证流程交互的函数。

```jsx useOAuth Hook icon=logos:react
import { useOAuth } from '@arcblock/did-connect-react/lib/OAuth';

function SocialLoginButtons() {
  const { loginOAuth, getOAuthConfigList, bindAuthLoading } = useOAuth();
  const [providers, setProviders] = React.useState([]);

  React.useEffect(() => {
    getOAuthConfigList().then(setProviders);
  }, [getOAuthConfigList]);

  const handleLogin = async (provider) => {
    try {
      const result = await loginOAuth({ provider: provider.provider });
      console.log('Login successful:', result);
      // Handle login result, e.g., update session
    } catch (error) {
      console.error('Login failed:', error.message);
    }
  };

  if (providers.length === 0) {
    return <p>No third-party login methods configured.</p>;
  }

  return (
    <div>
      {providers.map((p) => (
        <button key={p.provider} onClick={() => handleLogin(p)} disabled={bindAuthLoading}>
          Login with {p.provider}
        </button>
      ))}
    </div>
  );
}
```

### 核心函数

- **`loginOAuth({ provider }, { action, ... })`**：为特定提供商启动登录流程。将打开一个弹出窗口供用户进行身份验证。
- **`bindOAuth({ session, oauthItem })`**：将新的社交帐户绑定到当前登录的用户。
- **`unbindOAuth({ session, connectedAccount })`**：从当前用户解绑社交帐户。
- **`getOAuthConfigList()`**：异步获取在 Blocklet 设置中配置的已启用 OAuth 提供商列表。
- **`switchOAuthPassport(user)`**：打开一个对话框，用于在与同一 OAuth 身份关联的不同用户帐户（通行证）之间切换。

---

## Passkeys（无密码）

使用基于 WebAuthn 标准的 Passkeys 启用现代、安全且无密码的身份验证。这允许用户通过生物识别（指纹、面部 ID）、设备 PIN 或安全密钥登录。

### 1. 使用 PasskeyProvider 进行设置

使用 `PasskeyProvider` 包裹您的应用程序以启用 Passkey 功能。

```jsx PasskeyProvider Setup icon=logos:react
import { SessionProvider } from '@arcblock/did-connect-react';
import { PasskeyProvider } from '@arcblock/did-connect-react/lib/Passkey';

function App() {
  return (
    <SessionProvider>
      <PasskeyProvider
        onAddPasskey={(result) => console.log('Passkey added!', result)}
        onRemovePasskey={(result) => console.log('Passkey removed!', result)}>
        {/* Your application components */}
      </PasskeyProvider>
    </SessionProvider>
  );
}
```

**PasskeyProvider 的 Props**

<x-field-group>
  <x-field data-name="children" data-type="ReactNode" data-required="true" data-desc="将有权访问 Passkey 上下文的子组件。"></x-field>
  <x-field data-name="locale" data-type="string" data-default="en" data-desc="用于 UI 消息和提示的语言。"></x-field>
  <x-field data-name="onAddPasskey" data-type="function" data-desc="用户成功添加 Passkey 后触发的回调函数。"></x-field>
  <x-field data-name="onRemovePasskey" data-type="function" data-desc="用户成功移除 Passkey 后触发的回调函数。"></x-field>
  <x-field data-name="onSwitchPassport" data-type="function" data-desc="用户切换通行证后触发的回调函数。"></x-field>
  <x-field data-name="session" data-type="object" data-desc="当前用户会话对象，切换通行证 UI 时需要。"></x-field>
</x-field-group>

### 2. 使用 usePasskey Hook

`usePasskey` Hook 提供了 Passkey 注册和身份验证所需的所有必要函数。

```jsx usePasskey Hook icon=logos:react
import { usePasskey } from '@arcblock/did-connect-react/lib/Passkey';
import { useSession } from '@arcblock/did-connect-react';

function PasskeyAuth() {
  const { loginPasskey, connectPasskey, connecting } = usePasskey();
  const { session } = useSession();

  const handleLogin = async () => {
    try {
      const result = await loginPasskey();
      console.log('Passkey login successful:', result);
      // Handle login result
    } catch (error) {
      console.error('Passkey login failed:', error.message);
    }
  };

  const handleConnect = async () => {
    // This should be called when a user is already logged in
    // and wants to add a passkey in their profile settings.
    if (!session.user) {
      alert('You must be logged in to add a passkey.');
      return;
    }
    try {
      await connectPasskey();
    } catch (error) {
      console.error('Failed to connect passkey:', error.message);
    }
  };

  return (
    <div>
      <button onClick={handleLogin}>Login with Passkey</button>
      {session.user && (
        <button onClick={handleConnect} disabled={connecting}>
          {connecting ? 'Connecting...' : 'Add a Passkey'}
        </button>
      )}
    </div>
  );
}
```

### 核心函数
- **`loginPasskey({ action, ... })`**：启动 Passkey 身份验证流程（也称为断言）。这用于登录现有用户。
- **`connectPasskey(extraParams)`**：一个高级函数，用于创建新的 Passkey 并将其绑定到当前用户的帐户。它处理完整的注册过程。
- **`disconnectPasskey({ session, connectedAccount })`**：成功验证后，从当前用户的帐户中移除一个 Passkey。
- **`createPasskey(params)`**：启动 WebAuthn 注册过程的底层函数。`connectPasskey` 内部使用此函数。
- **`verifyPasskey(params)`**：启动 WebAuthn 身份验证过程的底层函数。`loginPasskey` 和 `disconnectPasskey` 内部使用此函数。
- **`switchPassport(user)`**：打开一个对话框，用于在可能共享 Passkeys 的不同用户帐户（通行证）之间切换。

---

## 统一登录

统一登录允许一组关联的 Blocklet 应用程序（一个“统一登录站点群”）共享同一个登录会话。当用户登录到指定的“主”站点时，他们可以在任何“成员”站点上被自动验证，而无需再次登录。

### 1. 概念与设置

此功能依赖于您 Blocklet 设置中的主-成员站点配置。`FederatedProvider` 组件是处理此功能在现代浏览器中正常工作所需的跨域通信所必需的，特别是在管理第三方 Cookie 访问方面。

与其他 Provider 一样，`FederatedProvider` 应包含在您的组件树中，通常位于 `SessionProvider` 内部。

```jsx FederatedProvider Setup icon=logos:react
import { SessionProvider } from '@arcblock/did-connect-react';
import { FederatedProvider } from '@arcblock/did-connect-react/lib/Federated';

function App() {
  return (
    <SessionProvider>
      <FederatedProvider>
        {/* Your application components */}
      </FederatedProvider>
    </SessionProvider>
  );
}
```

### 2. 使用方法

统一登录逻辑直接集成在会话管理中。您可以使用 `useSession` 返回的 `session` 对象上的 `loginFederated` 函数来触发它。

`loginFederated` 支持两种模式：
- **`auto`**：如果用户在主站点上拥有活动会话，则尝试静默登录用户。这非常适合在页面加载时进行检查。
- **`manual`**：启动标准的 DID Connect 登录流程。成功登录后，它还会与主站点建立统一登录会话。

```jsx Federated Login Example icon=logos:react
import { useSession } from '@arcblock/did-connect-react';
import React from 'react';

function FederatedLogin() {
  const { session } = useSession();
  const { federatedEnabled, loginFederated } = session;

  // Attempt auto-login on component mount
  React.useEffect(() => {
    if (federatedEnabled) {
      loginFederated((err) => {
        if (err) {
          console.warn('Auto federated login failed:', err);
        } else {
          console.log('Auto federated login successful!');
        }
      }, { mode: 'auto' });
    }
  }, [federatedEnabled, loginFederated]);

  const handleManualLogin = () => {
    loginFederated((err) => {
      if (err) console.error('Manual federated login failed:', err);
    }, { mode: 'manual' });
  };

  if (!federatedEnabled) {
    return <p>Federated login is not enabled.</p>;
  }

  return (
    <button onClick={handleManualLogin}>
      Login with Master Site Account
    </button>
  );
}
```