Metadata-Version: 2.4
Name: two-factor-service
Version: 1.0.0
Summary: 2FA Service Python SDK - 다중 채널 2단계 인증 서비스
Author-email: IDEACODE <biz@ideacode.co.kr>
License: MIT
Project-URL: Homepage, https://github.com/ideacode/securekey-rails
Project-URL: Documentation, https://github.com/ideacode/securekey-rails#readme
Project-URL: Repository, https://github.com/ideacode/securekey-rails
Keywords: 2fa,two-factor,authentication,otp,totp,sms,email,webauthn,passkey,push
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Dynamic: license-file

# two-factor-service

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

## 설치

```bash
pip install two-factor-service
```

## 빠른 시작

### 비동기 (async)

```python
from two_factor_service import TwoFactorClient

client = TwoFactorClient(
    api_key="your-api-key",
    base_url="https://securekey.ideacode.co.kr",
)

# SMS OTP 발송
result = await client.sms.send(SendSmsOtpOptions(
    phone="01012345678",
    purpose="login",
))

# SMS OTP 검증
verify = await client.sms.verify(VerifySmsOtpOptions(
    phone="01012345678",
    code="123456",
    purpose="login",
))

print(verify.verified)  # True
```

### 동기 (sync)

```python
from two_factor_service import TwoFactorClientSync

client = TwoFactorClientSync(
    api_key="your-api-key",
    base_url="https://securekey.ideacode.co.kr",
)

result = client.sms.send(SendSmsOtpOptions(
    phone="01012345678",
    purpose="login",
))
```

## 지원 채널

| 채널 | 메서드 | 설명 |
|------|--------|------|
| **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.register_start()` / `authenticate_start()` | FIDO2/Passkey 생체인증 |
| **Push** | `client.push.send()` / `verify()` / `wait_for_approval()` | 푸시 알림 인증 |

## SMS OTP

```python
from two_factor_service import TwoFactorClient, SendSmsOtpOptions, VerifySmsOtpOptions

client = TwoFactorClient(api_key="your-api-key", base_url="https://securekey.ideacode.co.kr")

# 발송
result = await client.sms.send(SendSmsOtpOptions(
    phone="01012345678",
    purpose="login",
    external_user_id="user-123",  # 선택
    service_name="MyApp",         # 선택
))
print(result.request_id, result.expires_at)

# 검증
verify = await client.sms.verify(VerifySmsOtpOptions(
    phone="01012345678",
    code="123456",
    purpose="login",
))
print(verify.verified, verify.verified_at)

# 상태 확인
status = await client.sms.status("01012345678")
print(status.has_pending_otp)
```

## Email OTP

```python
from two_factor_service import SendEmailOtpOptions, VerifyEmailOtpOptions

result = await client.email.send(SendEmailOtpOptions(
    email="user@example.com",
    purpose="signup",
))

verify = await client.email.verify(VerifyEmailOtpOptions(
    email="user@example.com",
    code="123456",
    purpose="signup",
))
```

## TOTP (시간 기반 OTP)

```python
from two_factor_service import TotpSetupOptions, TotpVerifyOptions

# 설정 (QR 코드 생성)
setup = await client.totp.setup(TotpSetupOptions(
    user_id="user-123",
    account_name="user@example.com",
))
print(setup.secret, setup.qr_code_data_url, setup.backup_codes)

# 설정 검증
await client.totp.verify_setup(TotpVerifyOptions(user_id="user-123", code="123456"))

# 이후 로그인 시 검증
verify = await client.totp.verify(TotpVerifyOptions(user_id="user-123", code="654321"))

# 백업 코드 재생성
new_codes = await client.totp.regenerate_backup_codes("user-123")

# TOTP 해제
await client.totp.remove("user-123")
```

## WebAuthn (Passkey)

```python
from two_factor_service import (
    WebAuthnRegisterStartOptions, WebAuthnRegisterFinishOptions,
    WebAuthnAuthenticateStartOptions, WebAuthnAuthenticateFinishOptions,
)

# 등록 시작
options = await client.webauthn.register_start(WebAuthnRegisterStartOptions(
    user_id="user-123",
    user_name="user@example.com",
    user_display_name="홍길동",
))

# 브라우저에서 credential 생성 후
credential = await client.webauthn.register_finish(WebAuthnRegisterFinishOptions(
    user_id="user-123",
    response=credential_response,
    device_name="MacBook Pro",
))

# 인증 시작
auth_options = await client.webauthn.authenticate_start(
    WebAuthnAuthenticateStartOptions(user_id="user-123")
)

# 브라우저에서 assertion 생성 후
verify = await client.webauthn.authenticate_finish(WebAuthnAuthenticateFinishOptions(
    user_id="user-123",
    response=assertion_response,
))

# 등록된 키 목록 / 삭제
credentials = await client.webauthn.list_credentials("user-123")
await client.webauthn.delete_credential("user-123", "credential-id")
```

## Push 인증

```python
from two_factor_service import PushRegisterDeviceOptions, PushSendOptions

# 기기 등록
device = await client.push.register_device(PushRegisterDeviceOptions(
    user_id="user-123",
    fcm_token="firebase-token",
    device_name="iPhone 15",
    platform="ios",
))

# 푸시 인증 요청
result = await client.push.send(PushSendOptions(
    user_id="user-123",
    title="로그인 인증 요청",
    body="새로운 기기에서 로그인을 시도합니다.",
))

# 승인 대기 (폴링)
verify = await client.push.wait_for_approval(
    result.request_id,
    timeout=60,
    interval=2.0,
)
print(verify.verified, verify.status)
```

## 설정 옵션

```python
client = TwoFactorClient(
    api_key="your-api-key",                          # 필수
    base_url="https://securekey.ideacode.co.kr",     # 기본: http://localhost:8090
    timeout=30.0,                                     # 요청 타임아웃 (초), 기본: 30.0
    retries=3,                                        # 재시도 횟수, 기본: 3
    retry_delay=1.0,                                  # 재시도 간격 (초), 기본: 1.0
)
```

## 에러 처리

```python
from two_factor_service import TwoFactorClient, TwoFactorError, VerifySmsOtpOptions

try:
    await client.sms.verify(VerifySmsOtpOptions(
        phone="01012345678", code="wrong"
    ))
except TwoFactorError as e:
    print(e.code)         # 'INVALID_OTP'
    print(e.message)      # '유효하지 않은 인증 코드입니다'
    print(e.status_code)  # 400
```

## 요구 사항

- Python 3.8 이상
- 의존성: `httpx` (비동기 HTTP 클라이언트)

## 라이선스

MIT — IDEACODE
