> ## Documentation Index
> Fetch the complete documentation index at: https://daehan-base.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 사용자 인증

> 사용자가 'Base로 로그인' 버튼을 클릭하여 온체인 어카운트 소유권을 증명하고, 비밀번호 없이 개방형 표준으로 서버에 세션을 생성하는 방법을 알아보세요.

export const SignInWithBaseButton = ({colorScheme = 'light'}) => {
  const isLight = colorScheme === 'light';
  return <button type="button" style={{
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: '8px',
    padding: '12px 16px',
    backgroundColor: isLight ? '#ffffff' : '#000000',
    border: 'none',
    borderRadius: '8px',
    cursor: 'pointer',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    fontSize: '14px',
    fontWeight: '500',
    color: isLight ? '#000000' : '#ffffff',
    minWidth: '180px',
    height: '44px'
  }}>
      <div style={{
    width: '16px',
    height: '16px',
    backgroundColor: isLight ? '#0000FF' : '#FFFFFF',
    borderRadius: '2px',
    flexShrink: 0
  }} />
      <span>Sign in with Base</span>
    </button>;
};

export const BasePayButton = ({colorScheme = 'light'}) => {
  const isLight = colorScheme === 'light';
  return <button type="button" style={{
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    padding: '12px 16px',
    backgroundColor: isLight ? '#ffffff' : '#0000FF',
    border: 'none',
    borderRadius: '8px',
    cursor: 'pointer',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    minWidth: '180px',
    height: '44px'
  }}>
      <img src={isLight ? '/images/base-account/BasePayBlueLogo.png' : '/images/base-account/BasePayWhiteLogo.png'} alt="Base Pay" style={{
    height: '20px',
    width: 'auto'
  }} />
    </button>;
};

## 비밀번호 대신 지갑 서명을 사용하는 이유

1. **새로운 비밀번호 불필요** – 사용자가 이미 보유한 키로 인증이 이루어집니다.
2. **탈취하거나 재사용할 것이 없음** – 각 로그인은 사용자 디바이스를 벗어나지 않는 일회성 도메인 바인딩 서명입니다.
3. **지갑 무관** – 모든 EIP-1193 지갑(브라우저 확장, 모바일 딥링크, 임베디드 프로바이더)에서 동작하며 개방형 ["Sign in with Ethereum" (SIWE) EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) 표준을 따릅니다.

Base Account는 이 표준을 기반으로 구축되어 있어 기존 SIWE 도구를 재사용하면서도 패스키, 세션 키, 스마트 지갑 보안의 이점을 누릴 수 있습니다.

<Warning>
  **브랜드 가이드라인을 준수해 주세요**

  `SignInWithBaseButton`을 사용하려면 애플리케이션 전반의 일관성을 위해 Base 브랜드 가이드라인을 준수해 주세요.
</Warning>

<Tip>
  **영상 콘텐츠를 선호하시나요?**

  이 페이지 [마지막 섹션](#video-guide)에서 구현 내용을 자세히 다루는 영상 가이드를 확인하세요.
</Tip>

## 전체 흐름

```mermaid theme={null}
sequenceDiagram
    participant User
    participant Browser
    participant AppServer as "App Server"
    participant SDK
    participant Account

    alt Generate locally
        Browser->>Browser: randomNonce()
    else Prefetch
        Browser->>AppServer: GET /auth/nonce (on page load)
        AppServer-->>Browser: nonce
    end

    User->>Browser: Click "Sign in with Base"
    Browser->>SDK: wallet_connect(signInWithEthereum {nonce})
    SDK->>Account: wallet_connect(...)
    User->>Account: Approve connection
    Account-->>SDK: {address, message, signature}
    SDK-->>Browser: {address, message, signature}

    Browser-->>AppServer: POST /auth/verify {address, message, signature}
    AppServer-->>Browser: session token / JWT
```

<Note type="info">
  **미배포 스마트 지갑?**

  Base Account 서명에는 [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) 래퍼가 포함되어 있어 지갑 컨트랙트가 배포되기 전에도 검증이 가능합니다. Viem의 [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage)와 [`verifyTypedData`](https://viem.sh/docs/actions/public/verifyTypedData)가 이를 자동으로 처리합니다.
</Note>

## 구현

### 의존성 설치

의존성을 설치합니다:

<CodeGroup>
  ```bash npm theme={null}
  npm install @base-org/account @base-org/account-ui
  ```

  ```bash pnpm theme={null}
  pnpm add @base-org/account @base-org/account-ui
  ```

  ```bash yarn theme={null}
  yarn add @base-org/account @base-org/account-ui
  ```

  ```bash bun theme={null}
  bun add @base-org/account @base-org/account-ui
  ```
</CodeGroup>

### 코드 스니펫

<CodeGroup>
  ```ts Browser (SDK) expandable theme={null}
  import { createBaseAccountSDK } from "@base-org/account";

  // Initialize the SDK
  const provider = createBaseAccountSDK({
    appName: "My App",
  }).getProvider();

  // 1 — get a fresh nonce (generate locally or prefetch from backend)
  const nonce = window.crypto.randomUUID().replace(/-/g, "");
  // OR prefetch from server
  // const nonce = await fetch("/auth/nonce").then((response) => response.text());

  // 2 — switch to Base Chain
  const switchChainResponse = await provider.request({
    method: "wallet_switchEthereumChain",
    params: [{ chainId: "0x2105" }],
  });

  console.log("Switch chain response:", switchChainResponse);

  // 3 — connect and authenticate
  try {
    const { accounts } = await provider.request({
      method: "wallet_connect",
      params: [
        {
          version: "1",
          capabilities: {
            signInWithEthereum: {
              nonce,
              chainId: "0x2105", // Base Mainnet - 8453
            },
          },
        },
      ],
    });

    const { address } = accounts[0];
    const { message, signature } =
      accounts[0].capabilities.signInWithEthereum;

    await fetch("/auth/verify", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ address, message, signature }),
    });
  } catch (error) {
    console.error("Failed to authenticate with Base Account:", error);
  }

  ```

  ```ts Backend (Viem) theme={null}
  import { createPublicClient, http } from 'viem';
  import { base } from 'viem/chains';

  const client = createPublicClient({ chain: base, transport: http() });

  export async function verifySig(req, res) {
    const { address, message, signature } = req.body;
    const valid = await client.verifyMessage({ address, message, signature });
    if (!valid) return res.status(401).json({ error: 'Invalid signature' });
    // create session / JWT
    res.json({ ok: true });
  }
  ```
</CodeGroup>

<Note type="tip">
  위 코드를 Base Account 외에도 사용하는 경우, 모든 지갑이 새로운 <code>wallet\_connect</code>{" "}
  메서드를 아직 지원하지 않는다는 점에 유의하세요.
  호출이 \[<code>method\_not\_supported</code>]를 발생시키면 <code>eth\_requestAccounts</code>와 <code>personal\_sign</code>을 사용하도록 폴백하세요.
</Note>

<Note type="tip">
  [팝업
  차단](/base-account/more/troubleshooting/usage-details/popups#default-blocking-behavior)을 피하려면
  사용자가 "Base로 로그인" 버튼을 누르기 <strong>전에</strong> (예: 페이지 로드 시) 논스를 가져오거나 생성하세요.
  보안을 위해, 백엔드가 모든 논스를 추적하고 재사용된 논스를 거부하기만 하면 됩니다. 논스의 출처는 관계없습니다.
</Note>

### Express 서버 예시

```ts title="server/auth.ts" expandable theme={null}
import crypto from "crypto";
import express from "express";
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";

const app = express();
app.use(express.json());

// Simple in-memory nonce store (swap for Redis or DB in production)
const nonces = new Set<string>();

app.get("/auth/nonce", (_, res) => {
  const nonce = crypto.randomBytes(16).toString("hex");
  nonces.add(nonce);
  res.send(nonce);
});

const client = createPublicClient({ chain: base, transport: http() });

app.post("/auth/verify", async (req, res) => {
  const { address, message, signature } = req.body;

  // 1. Check nonce hasn\'t been reused
  const nonce = message.match(/at (\w{32})$/)?.[1];
  if (!nonce || !nonces.delete(nonce)) {
    return res.status(400).json({ error: "Invalid or reused nonce" });
  }

  // 2. Verify signature
  const valid = await client.verifyMessage({ address, message, signature });
  if (!valid) return res.status(401).json({ error: "Invalid signature" });

  // 3. Create session / JWT here
  res.json({ ok: true });
});

app.listen(3001, () => console.log("Auth server listening on :3001"));
```

## Base Sign In With Base 버튼 추가

네이티브 룩앤필을 위해 사전 제작된 컴포넌트를 사용하세요:

```tsx title="App.tsx" theme={null}
import { SignInWithBaseButton } from "@base-org/account-ui/react";

export function App() {
  return (
    <SignInWithBaseButton
      colorScheme="light"
      onClick={() => signInWithBase()}
    />
  );
}
```

전체 props 및 테마 옵션은 `SignInWithBaseButton` 컴포넌트 타입과 이 페이지의 예시를 참고하세요.

<Warning>
  **브랜드 가이드라인을 준수해 주세요**

  `SignInWithBaseButton`을 사용하려면 애플리케이션 전반의 일관성을 위해 Base 브랜드 가이드라인을 준수해 주세요.
</Warning>

## 영상 가이드

<iframe width="560" height="315" src="https://www.youtube.com/embed/s9X_tUhkf9E?si=GOO5AkOyq7j-un9I" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen />
