npm package: @secondkey/node
Description: SecureKey Node.js SDK - 다중 채널 2단계 인증 서비스
Author: IDEACODE
Latest version: 1.1.0

# @secondkey/node

SecureKey 2FA Node.js SDK — 다중 채널 2단계 인증 서비스

## 설치

```bash
npm install @secondkey/node
```

## 빠른 시작

```typescript
import { TwoFactorClient } from '@secondkey/node';

const client = new TwoFactorClient({
  apiKey: 'your-api-key',
  baseUrl: 'https://securekey.ideacode.co.kr',
});

// SMS OTP 발송
const result = await client.sms.send({
  phone: '01012345678',
  purpose: 'login',
});

// SMS OTP 검증
const verify = await client.sms.verify({
  phone: '01012345678',
  code: '123456',
  purpose: 'login',
});

console.log(verify.verified); // true
```

## 지원 채널

| 채널 | 메서드 | 설명 |
|------|--------|------|
| **SMS** | `client.sms.send()` / `verify()` / `status()` | SMS OTP 발송 및 검증 |
| **Email** | `client.email.send()` / `verify()` / `status()` | 이메일 OTP 발송 및 검증 |
| **TOTP** | `client.totp.setup()` / `verify()` / `status()` | Google Authenticator 등 TOTP 앱 |
| **WebAuthn** | `client.webauthn.registerOptions()` / `authenticateOptions()` | FIDO2/Passkey 생체인증 |
| **Push** | `client.push.send()` / `verify()` / `waitForApproval()` | 푸시 알림 인증 |

## SMS OTP

```typescript
// 발송
const { requestId, expiresAt } = await client.sms.send({
  phone: '01012345678',
  purpose: 'login',
  externalUserId: 'user-123',     // 선택
  serviceName: 'MyApp',           // 선택
});

// 검증
const { verified, verifiedAt } = await client.sms.verify({
  phone: '01012345678',
  code: '123456',
  purpose: 'login',
});

// 상태 확인
const status = await client.sms.status('01012345678');
```

## Email OTP

```typescript
const { requestId } = await client.email.send({
  email: 'user@example.com',
  purpose: 'signup',
});

const { verified } = await client.email.verify({
  email: 'user@example.com',
  code: '123456',
  purpose: 'signup',
});
```

## TOTP (시간 기반 OTP)

```typescript
// 설정 (QR 코드 생성)
const { secret, uri, qrCodeDataUrl, backupCodes } = await client.totp.setup({
  userId: 'user-123',
  accountName: 'user@example.com',
});

// 설정 검증 (첫 코드 입력)
await client.totp.verifySetup({
  userId: 'user-123',
  code: '123456',
});

// 이후 로그인 시 검증
const { verified } = await client.totp.verify({
  userId: 'user-123',
  code: '654321',
});

// 백업 코드 재생성
const { backupCodes: newCodes } = await client.totp.regenerateBackupCodes('user-123');

// TOTP 해제
await client.totp.remove('user-123');
```

## WebAuthn (Passkey)

```typescript
// 등록 시작
const options = await client.webauthn.registerOptions({
  userId: 'user-123',
  userName: 'user@example.com',
  userDisplayName: '홍길동',
});

// 브라우저에서 navigator.credentials.create(options) 호출 후
const credential = await client.webauthn.registerVerify({
  userId: 'user-123',
  response: credentialResponse,
  deviceName: 'MacBook Pro',
});

// 인증 시작
const authOptions = await client.webauthn.authenticateOptions({
  userId: 'user-123',
});

// 브라우저에서 navigator.credentials.get(authOptions) 호출 후
const { verified } = await client.webauthn.authenticateVerify({
  userId: 'user-123',
  response: assertionResponse,
});

// 등록된 키 목록 / 삭제
const credentials = await client.webauthn.listCredentials('user-123');
await client.webauthn.deleteCredential('credential-id');
```

## Push 인증

```typescript
// 기기 등록
const { deviceId } = await client.push.registerDevice({
  userId: 'user-123',
  fcmToken: 'firebase-token',
  deviceName: 'iPhone 15',
  platform: 'ios',
});

// 푸시 인증 요청
const { requestId } = await client.push.send({
  userId: 'user-123',
  title: '로그인 인증 요청',
  body: '새로운 기기에서 로그인을 시도합니다.',
});

// 승인 대기 (폴링)
const { verified, status } = await client.push.waitForApproval(requestId, {
  timeout: 60000,
  interval: 2000,
});
```

## 설정 옵션

```typescript
const client = new TwoFactorClient({
  apiKey: 'your-api-key',           // 필수
  baseUrl: 'https://your-server',   // 기본: http://localhost:8090
  timeout: 30000,                   // 요청 타임아웃 (ms), 기본: 30000
  retries: 3,                       // 재시도 횟수, 기본: 3
  retryDelay: 1000,                 // 재시도 간격 (ms), 기본: 1000
});
```

## 에러 처리

```typescript
import { TwoFactorClient, TwoFactorError } from '@secondkey/node';

try {
  await client.sms.verify({ phone: '01012345678', code: 'wrong' });
} catch (error) {
  if (error instanceof TwoFactorError) {
    console.error(error.code);       // 'INVALID_OTP'
    console.error(error.message);    // '유효하지 않은 인증 코드입니다'
    console.error(error.statusCode); // 400
  }
}
```

## 요구 사항

- Node.js 16 이상
- 외부 의존성 없음 (제로 디펜던시)

## 라이선스

MIT — IDEACODE