SecureKey API를 다양한 언어에서 사용하는 방법입니다.
설치npm
npm install @secondkey/node
SMS OTP
import { TwoFactorClient } from '@secondkey/node';
const client = new TwoFactorClient({
apiKey: '2fa_your_api_key',
baseUrl: 'https://securekey.ideacode.co.kr'
});
// SMS 발송
const result = await client.sms.send({
phone: '01012345678',
purpose: 'login'
});
// SMS 검증
const verify = await client.sms.verify({
phone: '01012345678',
code: '123456'
});
console.log(verify.verified); // true
TOTP
// TOTP 설정 시작
const setup = await client.totp.setup({
userId: 'user_123',
accountName: '[email protected]'
});
// setup.uri → otpauth:// URI
// setup.qrCodeDataUrl → QR 코드 이미지 (base64 PNG)
// TOTP 검증
const verify = await client.totp.verify({
userId: 'user_123',
token: '123456'
});
Email OTP
// Email OTP 발송
const result = await client.email.send({
email: '[email protected]',
purpose: 'login'
});
// Email OTP 검증
const verify = await client.email.verify({
email: '[email protected]',
code: '123456'
});
console.log(verify.verified); // true
WebAuthn / Passkey
// 1. 등록 옵션 요청 (서버) — userName 필수
const regOptions = await client.webauthn.registerOptions({
userId: 'user_123',
userName: '[email protected]'
});
// 2. 브라우저에서 Credential 생성
const credential = await navigator.credentials.create({
publicKey: regOptions.publicKey
});
// 3. 등록 검증 (서버) — 브라우저 credential 필드를 최상위로 전달
const regResult = await client.webauthn.registerVerify({
userId: 'user_123',
id: credential.id,
rawId: btoa(String.fromCharCode(...new Uint8Array(credential.rawId))),
type: credential.type,
response: credential.response,
deviceName: 'Chrome on MacOS'
});
// 4. 인증 옵션 요청 (서버)
const authOptions = await client.webauthn.authenticateOptions({
userId: 'user_123'
});
// 5. 브라우저에서 Credential 사용
const assertion = await navigator.credentials.get({
publicKey: authOptions.publicKey
});
// 6. 인증 검증 (서버)
const authResult = await client.webauthn.authenticateVerify({
userId: 'user_123',
id: assertion.id,
rawId: btoa(String.fromCharCode(...new Uint8Array(assertion.rawId))),
type: assertion.type,
response: assertion.response
});
Push 인증
// 1. 기기 등록 (FCM 토큰)
await client.push.registerDevice({
userId: 'user_123',
fcmToken: 'fcm_token_from_firebase_sdk',
deviceName: 'iPhone 15',
platform: 'ios'
});
// 2. Push 인증 요청 발송
const push = await client.push.send({
userId: 'user_123',
title: '로그인 승인 요청',
body: '새로운 기기에서 로그인을 시도합니다.'
});
// 3. 사용자 기기에서 승인/거부 (모바일 앱)
await client.push.respond(push.requestId, 'approve');
// 4. 서버에서 결과 폴링 (requestId만 전달)
const result = await client.push.verify(push.requestId);
console.log(result.verified); // true
Webhook 수신
import crypto from 'crypto';
import express from 'express';
const app = express();
app.use(express.json());
app.post('/webhook/securekey', (req, res) => {
// HMAC 서명 검증
const signature = req.headers['x-securekey-signature'];
const expected = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest('hex');
if (!crypto.timingSafeEqual(
Buffer.from(signature), Buffer.from(expected)
)) {
return res.status(401).send('Invalid signature');
}
const { event, channel, data } = req.body;
console.log(channel + ' 인증 완료: ' + data.externalUserId);
res.sendStatus(200);
});
설치pip
pip install two-factor-service
SMS OTP
import asyncio
from two_factor_service import (
TwoFactorClient, SendSmsOtpOptions, VerifySmsOtpOptions
)
client = TwoFactorClient(
api_key="2fa_your_api_key",
base_url="https://securekey.ideacode.co.kr"
)
async def main():
# SMS 발송
result = await client.sms.send(SendSmsOtpOptions(
phone="01012345678", purpose="login"
))
# SMS 검증 — 결과는 VerifyResult dataclass
verify = await client.sms.verify(VerifySmsOtpOptions(
phone="01012345678", code="123456"
))
print(verify.verified) # True
asyncio.run(main())
TOTP
# async 함수 내에서 사용
from two_factor_service import TotpSetupOptions, TotpVerifyOptions
# TOTP 설정 — 결과는 TotpSetupResult dataclass
setup = await client.totp.setup(TotpSetupOptions(
user_id="user_123", account_name="[email protected]"
))
# setup.uri → otpauth:// URI
# setup.qr_code_data_url → QR 코드 이미지
# TOTP 검증
verify = await client.totp.verify(TotpVerifyOptions(
user_id="user_123", code="123456"
))
Email OTP
# async 함수 내에서 사용
from two_factor_service import SendEmailOtpOptions, VerifyEmailOtpOptions
# Email OTP 발송
result = await client.email.send(SendEmailOtpOptions(
email="[email protected]", purpose="login"
))
# Email OTP 검증
verify = await client.email.verify(VerifyEmailOtpOptions(
email="[email protected]", code="123456"
))
print(verify.verified) # True
WebAuthn / Passkey
# async 함수 내에서 사용
from two_factor_service import (
WebAuthnRegisterStartOptions, WebAuthnRegisterFinishOptions,
WebAuthnAuthenticateStartOptions, WebAuthnAuthenticateFinishOptions,
)
# 1. 등록 옵션 요청 — user_name 필수
reg_options = await client.webauthn.register_start(
WebAuthnRegisterStartOptions(
user_id="user_123", user_name="[email protected]"
)
)
# reg_options["publicKey"] → 브라우저에 전달
# 2. 등록 검증 — 브라우저 credential 필드 포함
reg_result = await client.webauthn.register_finish(
WebAuthnRegisterFinishOptions(
user_id="user_123",
response=credential_response,
id=credential_id,
raw_id=credential_raw_id,
device_name="Chrome on MacOS"
)
)
# 3. 인증 옵션 요청
auth_options = await client.webauthn.authenticate_start(
WebAuthnAuthenticateStartOptions(user_id="user_123")
)
# 4. 인증 검증
auth_result = await client.webauthn.authenticate_finish(
WebAuthnAuthenticateFinishOptions(
user_id="user_123",
response=assertion_response,
id=assertion_id,
raw_id=assertion_raw_id,
)
)
Push 인증
# async 함수 내에서 사용
from two_factor_service import PushRegisterDeviceOptions, PushSendOptions
# 1. 기기 등록
await client.push.register_device(PushRegisterDeviceOptions(
user_id="user_123",
fcm_token="fcm_token_from_firebase",
device_name="Galaxy S24",
platform="android"
))
# 2. Push 인증 요청 발송 — 결과는 PushSendResult dataclass
push = await client.push.send(PushSendOptions(
user_id="user_123",
title="로그인 승인 요청",
body="새로운 기기에서 로그인을 시도합니다."
))
# 3. 결과 폴링 검증 — 결과는 PushVerifyResult dataclass
result = await client.push.verify(push.request_id)
print(result.verified) # True
Webhook 수신
import hmac, hashlib, json
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret"
@app.post("/webhook/securekey")
def webhook():
# HMAC 서명 검증
signature = request.headers.get("X-SecureKey-Signature", "")
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(),
request.get_data(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
abort(401)
payload = request.get_json()
print(f"{payload['channel']} 인증 완료: {payload['data']['externalUserId']}")
return "", 200
HTTP 직접 호출 예제입니다. OpenAPI 명세로 클라이언트 코드를 자동 생성할 수도 있습니다.
의존성 (build.gradle)OkHttp
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.google.code.gson:gson:2.10.1'
SMS OTP
OkHttpClient client = new OkHttpClient();
String BASE_URL = "https://securekey.ideacode.co.kr/api/v1";
// SMS 발송
RequestBody sendBody = RequestBody.create(
"{\"phone\":\"01012345678\",\"purpose\":\"login\"}",
MediaType.parse("application/json")
);
Request sendReq = new Request.Builder()
.url(BASE_URL + "/otp/send")
.addHeader("X-API-Key", "2fa_your_api_key")
.post(sendBody).build();
Response sendRes = client.newCall(sendReq).execute();
// SMS 검증
RequestBody verifyBody = RequestBody.create(
"{\"phone\":\"01012345678\",\"code\":\"123456\"}",
MediaType.parse("application/json")
);
Request verifyReq = new Request.Builder()
.url(BASE_URL + "/otp/verify")
.addHeader("X-API-Key", "2fa_your_api_key")
.post(verifyBody).build();
Response verifyRes = client.newCall(verifyReq).execute();
Email OTP
// Email OTP 발송
RequestBody emailBody = RequestBody.create(
"{\"email\":\"[email protected]\",\"purpose\":\"login\"}",
MediaType.parse("application/json")
);
Request emailReq = new Request.Builder()
.url(BASE_URL + "/email-otp/send")
.addHeader("X-API-Key", "2fa_your_api_key")
.post(emailBody).build();
Response emailRes = client.newCall(emailReq).execute();
// Email OTP 검증
RequestBody emailVerifyBody = RequestBody.create(
"{\"email\":\"[email protected]\",\"code\":\"123456\"}",
MediaType.parse("application/json")
);
Request emailVerifyReq = new Request.Builder()
.url(BASE_URL + "/email-otp/verify")
.addHeader("X-API-Key", "2fa_your_api_key")
.post(emailVerifyBody).build();
Response emailVerifyRes = client.newCall(emailVerifyReq).execute();
WebAuthn / Passkey
// 1. 등록 옵션 요청
RequestBody regBody = RequestBody.create(
"{\"userId\":\"user_123\"}",
MediaType.parse("application/json")
);
Request regReq = new Request.Builder()
.url(BASE_URL + "/webauthn/register/options")
.addHeader("X-API-Key", "2fa_your_api_key")
.post(regBody).build();
Response regRes = client.newCall(regReq).execute();
// → publicKey 옵션을 브라우저에 전달
// 2. 등록 검증 (브라우저에서 받은 credential JSON 전달)
Request regVerifyReq = new Request.Builder()
.url(BASE_URL + "/webauthn/register/verify")
.addHeader("X-API-Key", "2fa_your_api_key")
.post(credentialBody).build();
Response regVerifyRes = client.newCall(regVerifyReq).execute();
// 3. 인증도 동일 패턴: /webauthn/authenticate/options → /webauthn/authenticate/verify
Push 인증
// 1. 기기 등록
RequestBody deviceBody = RequestBody.create(
"{\"userId\":\"user_123\",\"fcmToken\":\"token\",\"platform\":\"android\"}",
MediaType.parse("application/json")
);
Request deviceReq = new Request.Builder()
.url(BASE_URL + "/push/devices")
.addHeader("X-API-Key", "2fa_your_api_key")
.post(deviceBody).build();
client.newCall(deviceReq).execute();
// 2. Push 인증 요청 발송
RequestBody pushBody = RequestBody.create(
"{\"userId\":\"user_123\",\"title\":\"로그인 승인 요청\"}",
MediaType.parse("application/json")
);
Request pushReq = new Request.Builder()
.url(BASE_URL + "/push/send")
.addHeader("X-API-Key", "2fa_your_api_key")
.post(pushBody).build();
Response pushRes = client.newCall(pushReq).execute();
// → requestId를 저장 후 /push/verify로 폴링
HTTP 직접 호출 예제입니다. OpenAPI 명세로 클라이언트 코드를 자동 생성할 수도 있습니다.
SMS OTP
package main
import (
"bytes"
"encoding/json"
"net/http"
)
const baseURL = "https://securekey.ideacode.co.kr/api/v1"
const apiKey = "2fa_your_api_key"
func sendSMS(phone, purpose string) (*http.Response, error) {
body, _ := json.Marshal(map[string]string{
"phone": phone, "purpose": purpose,
})
req, _ := http.NewRequest("POST", baseURL+"/otp/send",
bytes.NewBuffer(body))
req.Header.Set("X-API-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
func verifySMS(phone, code string) (*http.Response, error) {
body, _ := json.Marshal(map[string]string{
"phone": phone, "code": code,
})
req, _ := http.NewRequest("POST", baseURL+"/otp/verify",
bytes.NewBuffer(body))
req.Header.Set("X-API-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
Email OTP
func sendEmailOTP(email, purpose string) (*http.Response, error) {
body, _ := json.Marshal(map[string]string{
"email": email, "purpose": purpose,
})
req, _ := http.NewRequest("POST", baseURL+"/email-otp/send",
bytes.NewBuffer(body))
req.Header.Set("X-API-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
func verifyEmailOTP(email, code string) (*http.Response, error) {
body, _ := json.Marshal(map[string]string{
"email": email, "code": code,
})
req, _ := http.NewRequest("POST", baseURL+"/email-otp/verify",
bytes.NewBuffer(body))
req.Header.Set("X-API-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
WebAuthn / Passkey
// 1. 등록 옵션 요청
func webauthnRegisterOptions(userID string) (*http.Response, error) {
body, _ := json.Marshal(map[string]string{
"userId": userID,
})
req, _ := http.NewRequest("POST", baseURL+"/webauthn/register/options",
bytes.NewBuffer(body))
req.Header.Set("X-API-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// 2. 등록 검증: POST /webauthn/register/verify
// 3. 인증 옵션: POST /webauthn/authenticate/options
// 4. 인증 검증: POST /webauthn/authenticate/verify
// (모두 동일한 패턴으로 호출)
Push 인증
// 기기 등록
func registerPushDevice(userID, fcmToken string) (*http.Response, error) {
body, _ := json.Marshal(map[string]string{
"userId": userID,
"fcmToken": fcmToken,
"platform": "android",
})
req, _ := http.NewRequest("POST", baseURL+"/push/devices",
bytes.NewBuffer(body))
req.Header.Set("X-API-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// Push 인증 발송
func sendPushAuth(userID, title string) (*http.Response, error) {
body, _ := json.Marshal(map[string]string{
"userId": userID,
"title": title,
})
req, _ := http.NewRequest("POST", baseURL+"/push/send",
bytes.NewBuffer(body))
req.Header.Set("X-API-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
return http.DefaultClient.Do(req)
}
// 결과 폴링: POST /push/verify (requestId + userId)
HTTP 직접 호출 예제입니다. OpenAPI 명세로 클라이언트 코드를 자동 생성할 수도 있습니다.
SMS OTP
<?php
$baseUrl = 'https://securekey.ideacode.co.kr/api/v1';
$apiKey = '2fa_your_api_key';
function securekeyPost($path, $data) {
global $baseUrl, $apiKey;
$ch = curl_init($baseUrl . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . $apiKey,
],
CURLOPT_POSTFIELDS => json_encode($data),
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// SMS 발송
$result = securekeyPost('/otp/send', [
'phone' => '01012345678',
'purpose' => 'login'
]);
// SMS 검증
$verify = securekeyPost('/otp/verify', [
'phone' => '01012345678',
'code' => '123456'
]);
var_dump($verify['data']['verified']); // true
Email OTP
// Email OTP 발송
$result = securekeyPost('/email-otp/send', [
'email' => '[email protected]',
'purpose' => 'login'
]);
// Email OTP 검증
$verify = securekeyPost('/email-otp/verify', [
'email' => '[email protected]',
'code' => '123456'
]);
var_dump($verify['data']['verified']); // true
WebAuthn / Passkey
// 1. 등록 옵션 요청
$regOptions = securekeyPost('/webauthn/register/options', [
'userId' => 'user_123'
]);
// $regOptions['data']['publicKey'] → 브라우저에 전달
// 2. 등록 검증 (브라우저에서 받은 credential 전달)
$regResult = securekeyPost('/webauthn/register/verify', [
'userId' => 'user_123',
'credential' => $credentialFromBrowser
]);
// 3. 인증: /webauthn/authenticate/options → /webauthn/authenticate/verify
Push 인증
// 기기 등록
$device = securekeyPost('/push/devices', [
'userId' => 'user_123',
'fcmToken' => 'fcm_token_from_firebase',
'platform' => 'android'
]);
// Push 인증 요청 발송
$push = securekeyPost('/push/send', [
'userId' => 'user_123',
'title' => '로그인 승인 요청',
'body' => '새로운 기기에서 로그인을 시도합니다.'
]);
$requestId = $push['data']['requestId'];
// 결과 폴링 검증
$result = securekeyPost('/push/verify', [
'userId' => 'user_123',
'requestId' => $requestId
]);
var_dump($result['data']['verified']); // true
.NET 8+ HttpClient 기반 예제입니다. 전체 실행 가능한 프로젝트는 examples/csharp/ 디렉토리를 참고하세요.
공통 설정.NET 8
using var client = new HttpClient {
BaseAddress = new Uri("https://securekey.ideacode.co.kr")
};
client.DefaultRequestHeaders.Add("X-API-Key", "2fa_your_api_key");
SMS OTP
// SMS 발송
var response = await client.PostAsJsonAsync("/api/v1/otp/send", new {
phone = "01012345678",
purpose = "login"
});
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
// SMS 검증
var verifyResponse = await client.PostAsJsonAsync("/api/v1/otp/verify", new {
phone = "01012345678",
code = "123456"
});
Email OTP
// Email OTP 발송
var emailRes = await client.PostAsJsonAsync("/api/v1/email-otp/send", new {
email = "[email protected]",
purpose = "login"
});
// Email OTP 검증
var emailVerify = await client.PostAsJsonAsync("/api/v1/email-otp/verify", new {
email = "[email protected]",
code = "123456"
});
TOTP
// TOTP 설정
var setup = await client.PostAsJsonAsync("/api/v1/totp/setup", new {
userId = "user_123",
issuer = "MyApp",
accountName = "[email protected]"
});
// TOTP 검증
var verify = await client.PostAsJsonAsync("/api/v1/totp/verify", new {
userId = "user_123",
token = "123456"
});
Push 인증
// 기기 등록
await client.PostAsJsonAsync("/api/v1/push/devices", new {
userId = "user_123",
fcmToken = "fcm_token",
platform = "android"
});
// Push 인증 요청 발송
var push = await client.PostAsJsonAsync("/api/v1/push/send", new {
userId = "user_123",
title = "로그인 승인 요청"
});
// 결과 폴링: POST /api/v1/push/verify
Webhook HMAC 검증
using System.Security.Cryptography;
string ComputeSignature(string secret, string payload) {
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
return "sha256=" + Convert.ToHexString(hash).ToLowerInvariant();
}
// X-SecureKey-Signature 헤더와 비교하여 검증
인증 방식
X-API-Key
API Key를 헤더로 전달 (권장)
Authorization: Bearer
Bearer 토큰 방식도 지원
응답 형식
// 성공
{ "success": true, "message": "...", "data": { ... } }
// 실패
{ "success": false, "error": "...", "code": "ERROR_CODE" }
// MFA 정책에 의한 추가 인증 요구
{ "success": false, "code": "MFA_REQUIRED",
"requiredChannels": ["totp"], "matchedPolicies": ["결제 시 추가 인증"] }
Rate Limiting
전역: 100 req/min per IP
OTP 발송: 3 req/min per IP
인증 시도: 10 req/min per IP
초과 시 429 Too Many Requests 반환