자체 UI에 CP 인증 붙이기 — @cp-platform/auth-sdk
외부 product (your product 처럼 별도 도메인에서 운영되는 앱) 가 자체 로그인 화면을 유지하면서 CP Platform 사용자 풀을 그대로 쓰는 표준 방식입니다.
SDK 한 줄 install로 끝:
npm i git+https://github.com/Copark-Dev/cp-auth-sdk.git#v0.5.2왜 SDK인가: 이전에는 각 product가 PKCE / code-verifier / popup callback / in-app browser 차단 같은 걸 직접 짰습니다. 이제 SDK가 다 wrap. Threads / Toss / KakaoBank / 쿠팡 / 배민 / 당근 / Discord 등 30+ 인앱 브라우저 가드가 자동으로 적용됩니다.
1. Frontend — 로그인 / 가드 / OAuth
App root에 Provider 한 번:
import { CpAuthProvider } from "@cp-platform/auth-sdk/react";
<CpAuthProvider strict>
<App />
</CpAuthProvider>모든 OAuth 버튼 클릭 핸들러 한 줄:
import { useCpAuth } from "@cp-platform/auth-sdk/react";
function LoginButton() {
const { guardOAuth } = useCpAuth();
return (
<button onClick={() => {
if (guardOAuth("google") !== "proceed") return; // 인앱 자동 처리
// ── 여기서부터 product 자체 OAuth 흐름 ──
window.location.href = `${CP_AUTH_URL}/oauth/authorize?client_id=...&...`;
}}>
Google로 계속하기
</button>
);
}guardOAuth("google") 반환:
"proceed"— 정상. 자체 흐름 진행."redirected"— Android 인앱이라 SDK가 자동으로 Chrome으로 escape. 페이지는 이미 unload 중 → caller는 즉시 return."blocked"— iOS 인앱이라 SDK가<InAppBrowserModal/>마운트. 사용자가 직접 Safari에서 다시 열어야 (Apple OS 정책).
Kakao 는 자동으로 "proceed" 반환합니다 (in-app webview 에서도 작동).
이메일/비번 직접 호출
import { CpAuthClient } from "@cp-platform/auth-sdk";
const auth = new CpAuthClient({
authUrl: "https://auth.cp-platform.com",
productOrigin: "https://your-app.com",
});
await auth.login(email, password);
await auth.signup({ email, password, phone, phone_consent: true });
await auth.requestPasswordReset(email);
await auth.confirmPasswordReset(tokenFromEmailLink, newPassword);
await auth.verifyEmail(tokenFromEmailLink);
await auth.logout();
if (auth.isLoggedIn()) {
const user = await auth.getMe();
}토큰은 SDK가 자동으로 localStorage 에 저장하고 만료 1분 전에 refresh. SSR / React Native 환경은 storage 옵션으로 커스텀 어댑터 주입 가능합니다 (SDK README 참고).
2. Backend — JWT 검증 + OAuth code exchange
Node / Express / Next API routes / Fastify 에서:
import { verifyCpJwt } from "@cp-platform/auth-sdk/jwt";
import { CpServerClient } from "@cp-platform/auth-sdk/server";
// 매 요청 인증 (middleware)
const { cpUserId, email } = await verifyCpJwt(bearerToken);
const { rows } = await db.query(
"SELECT id FROM profiles WHERE cp_user_id = $1",
[cpUserId]
);
// OAuth callback 처리
const cp = new CpServerClient(); // authUrl default = https://auth.cp-platform.com
const tokens = await cp.exchangeOAuthCode(code, codeVerifier);
const user = await cp.getUserInfo(tokens.access_token);
// getOrCreateLocalUser(user.sub) — 아래 Federated identity 패턴 참고verifyCpJwt 는 Supabase JWKS 로 ES256 검증. 자체 JWT secret 관리 안 해도 됩니다.
3. Federated identity — 사용자 풀 통합
product 자체 DB는 그대로 두고, CP user_id 만 매핑 컬럼으로 잇습니다:
ALTER TABLE public.profiles
ADD COLUMN cp_user_id UUID UNIQUE;backend 흐름:
- 요청 옴 →
verifyCpJwt(bearerToken)→cpUserId SELECT id FROM profiles WHERE cp_user_id = $1로 자체 user_id 조회- 없으면 자동 생성 (
INSERT ... (cp_user_id, email, ...)) - 이후 비즈니스 로직은 자체 user_id 사용
이러면 CP 가입자가 product 들어왔을 때 첫 로그인 만에 자동으로 product 안에 row 가 생깁니다. 비밀번호 / OAuth 토큰은 CP 쪽 한 곳에만 존재.
4. In-app browser 가드 (자동)
<CpAuthProvider strict> 가 마운트되어 있으면:
| 환경 | 동작 |
|---|---|
| Android 인앱 (Threads / KakaoTalk / Toss / 쿠팡 / 배민 / Discord / …) + Google·Apple | intent:// URL 로 Chrome 강제 호출. 사용자 클릭 0 |
| iOS 인앱 + Google·Apple | <InAppBrowserModal/> 자동 마운트. 사용자가 우상단 메뉴에서 Safari 로 직접 열도록 안내 (Apple OS 정책상 강제 호출 불가) |
| Kakao OAuth | 항상 통과 (Kakao 는 인앱에서도 작동) |
| 일반 모바일 / 데스크톱 브라우저 | 영향 없음 |
SDK 가 cover 하는 인앱:
- 글로벌: Threads(Barcelona codename) / Instagram / Facebook / WeChat / X / TikTok / Snapchat / Pinterest / Reddit / Discord / Telegram / Slack / LINE
- 한국: KakaoTalk / KakaoBank / KakaoPay / KakaoStory / Toss / Coupang / Baemin / Karrot / 11st / SSG / Emart / Lotte / Gmarket / Naver(검색·메일·카페) / Daum / BAND / EveryTime / Blind / KB / Woori / Shinhan / Hana / NH / IBK
- Long-tail:
strict: true일 때 화이트리스트(Safari / Chrome / Samsung / Firefox / Edge / Opera / Brave / DuckDuckGo) 외 모바일 UA 는 모두 인앱으로 간주.
5. 자체 구현에서 마이그레이션
기존에 PKCE / popup / code-verifier 를 직접 짰다면:
- SDK 설치 +
<CpAuthProvider>mount - OAuth 버튼 핸들러 첫 줄에
guardOAuth("google")가드 삽입 — 기존 PKCE / popup 흐름은 그대로 두어도 됩니다 (가드는 순수 추가) - (선택) 자체 PKCE / fetch 코드를
CpAuthClient호출로 점진 교체 - backend 의 자체
jose.jwtVerify또는 httpx 래퍼를verifyCpJwt/CpServerClient로 교체 .env의CP_JWT_*같은 변수는 SDK default 가 production 값을 갖고 있어 대부분 삭제 가능 (staging / local 만 override)
여러 product 를 이 방식으로 통합 인증에 연결할 수 있습니다. 마이그레이션 PR 은 팀 내부 저장소(<your-org>)에서 참고 사례로 관리하세요.
6. SDK 버전
v0.1.0 — 현재 stable. 모든 기능 포함:
CpAuthClient+verifyCpJwt+CpServerClient<CpAuthProvider>+useCpAuth().guardOAuth()- 30+ 인앱 브라우저 패턴 + strict 모드 + Android Chrome intent 자동 escape
외부 product 는 항상 v0.1.0 으로 pin 하면 됩니다.