Capacitor 앱에 인증을 추가하려면 인증 소스(자체 백엔드, Firebase/Supabase 같은 호스팅 서비스, Auth0/Okta/Entra ID 같은 OIDC 제공업체, 또는 Better Auth 같은 셀프 호스팅 라이브러리)를 선택하세요. 로그인 페이지를 여는 작업에는 native plugin을 사용하고, 서버에서 token을 검증한 뒤, 생성된 session을 Keychain이나 Keystore에 저장해야 합니다. 이후 생체 인증과 passkeys를 사용하면 다음 로그인부터는 매우 빠르게 접속할 수 있습니다.
이 가이드는 각 옵션에 적합한 Capacitor plugin과 워크플로우를 매핑해 드립니다. 로그인 호출, token 검증, session 저장, 생체 인증 해제와 같이 모든 앱에 공통적으로 적용되는 부분은 code로 표시했습니다. 이 내용은 Capacitor 8 (2026년 10월) 기준으로 최신화되었습니다. 각 제공업체별 상세 가이드는 아래 링크에서 확인하실 수 있습니다.
모든 Capacitor 앱이 거치는 인증 흐름
어떤 프로바이더를 선택하든 방식은 동일합니다:
- 사용자 식별 네이티브 소셜 시트, 시스템 브라우저의 로그인 페이지, passkey prompt 또는 이메일/비밀번호 입력 폼을 통해 사용자를 확인합니다.
- 인증 증명 획득: ID token, 인증 code, 또는 인증 서버로부터 세션 token을 받습니다.
- 서버에서 검증: signature, issuer, audience, expiry를 확인한 후 사용자를 생성하거나 조회합니다.
- 세션 발급: 자체 token 또는 프로바이더의 token을 refresh 전략과 함께 발급합니다.
- 안전하게 저장 기기에 저장하고 API 호출 시 함께 전달합니다.
- 빠른 잠금 해제 다음에는 생체 인증을 사용하고, 로그아웃 및 계정 삭제를 처리하세요.
1, 2단계는 제공업체마다 다릅니다. 3~6단계는 모든 옵션에서 직접 구현해야 하지만, SDK가 대부분을 처리해 주는 완전 호스팅형 BaaS는 예외입니다.
WebView 로그인 페이지가 작동하지 않는 이유
Capacitor 앱은 로컬 오리진을 가진 WKWebView(iOS) 또는 Android WebView에서 웹 code를 실행합니다(capacitor://localhost 또는 https://localhost). 해당 WebView 내에서 제공업체의 로그인 페이지를 로드하면 세 가지 문제가 발생합니다:
- Google에서 반환하는 오류:
disallowed_useragent페이지 표시를 거부합니다. - 앱이 해당 페이지가 실제 제공업체의 페이지임을 증명할 수 없기 때문입니다. 이것이 RFC 8252에서 네이티브 앱에 시스템 브라우저를 사용하도록 권장하는 이유입니다.
- 전체 페이지 리다이렉트는 번들링된 앱을 벗어나게 하여 상태(state)를 손실시킵니다.
따라서 아래의 모든 흐름은 네이티브 SDK (Apple, Google, Facebook) 또는 시스템 브라우저 세션을 사용합니다(ASWebAuthenticationSession iOS의 경우(Android는 Chrome Custom Tabs) 커스텀 스킴을 통해 앱으로 돌아옵니다.
한눈에 보는 옵션
| 접근 방식 | 추천 용도 | Capacitor 플러그인 | 백엔드 작업 | 가이드 |
|---|---|---|---|---|
| Apple로 로그인 | 서드파티 로그인을 사용하는 모든 iOS 앱 | @capgo/capacitor-social-login |
ID 토큰 검증 | Apple 가이드 |
| Google 로그인 | B2C 앱, Android 우선 | @capgo/capacitor-social-login |
ID 토큰 검증 | Google 가이드 |
| Auth0 | 다양한 연결을 지원하는 호스팅 로그인 | @capgo/capacitor-social-login (OAuth2) |
액세스 토큰 검증 | Auth0 가이드 |
| Okta | Workforce 및 B2B SSO | @capgo/capacitor-social-login (OAuth2) |
액세스 토큰 검증 | Okta 가이드 |
| Microsoft Entra ID | Microsoft 365 조직 | @capgo/capacitor-social-login (OAuth2) |
액세스 토큰 검증, Graph | Entra ID 가이드 |
| 더 나은 인증 방식 | 자체 호스팅, 사용자 DB 보유 | better-auth 클라이언트 + Social Login |
Better Auth 서버 실행 | Better Auth 가이드 |
| Supabase Auth | 내장 인증 기능이 포함된 Postgres 백엔드 | 소셜 로그인 + supabase-js |
최소한의 설정 | Supabase 가이드 |
| Firebase Authentication | 이미 Firebase를 사용 중인 앱 | @capgo/capacitor-firebase-authentication |
최소한의 설정 | Firebase 문서 |
| Passkeys | 자체 도메인에서 비밀번호 없는 로그인 | @capgo/capacitor-passkey |
WebAuthn 서버 | Passkey 문서 |
| 이메일 및 비밀번호 | 간단한 앱, 자체 백엔드 | 없음 (HTTP) | 해싱, 초기화, 속도 제한 | 이 아티클 |
네이티브 소셜 로그인 (Apple, Google, Facebook)
하나의 플러그인으로 주요 소비자용 프로바이더 3종과 범용 OAuth2 엔진을 모두 지원합니다:
bun add @capgo/capacitor-social-login
bunx cap sync
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
apple: { clientId: 'com.example.app' },
google: {
webClientId: 'WEB_CLIENT_ID.apps.googleusercontent.com',
iOSClientId: 'IOS_CLIENT_ID.apps.googleusercontent.com',
},
});
const { result } = await SocialLogin.login({ provider: 'google', options: {} });
// send result.idToken to your server
사용하지 않는 프로바이더는 다음에서 비활성화하여 capacitor.config.ts (plugins.SocialLogin.providers) 앱에 SDK가 포함되지 않도록 하세요. Facebook을 사용하지 않는다면 Facebook SDK의 광고 ID 권한도 함께 제거됩니다.
각 프로바이더마다 네이티브 설정이 필요합니다. Apple은 Xcode에서 Capability 설정과 Android용 Services ID가 필요하며, Google은 SHA-1 지문과 역방향 클라이언트 ID URL scheme이 필요합니다. Apple 및 Google 가이드에서 모든 단계와 일반적인 오류를 설명합니다.
OAuth 2.0 및 OpenID Connect를 이용한 엔터프라이즈 SSO
Auth0, Okta, Microsoft Entra ID, Keycloak, Cognito, OneLogin 또는 자체 OIDC 서버를 사용하는 경우, 플러그인의 oauth2 provider를 사용하세요. 이 기능은 PKCE를 포함한 인증 code 흐름을 실행하며, ASWebAuthenticationSession iOS 및 Android의 Custom Tabs에서 설정할 때 androidUseCustomTabs: true:
await SocialLogin.initialize({
oauth2: {
corp: {
issuerUrl: 'https://sso.example.com/realms/mobile', // OIDC discovery
clientId: 'mobile-app',
redirectUrl: 'com.example.app://oauth/corp',
scope: 'openid profile email offline_access',
pkceEnabled: true,
androidUseCustomTabs: true,
},
},
});
const { result } = await SocialLogin.login({
provider: 'oauth2',
options: { providerId: 'corp' },
});
동일한 map에 여러 provider를 등록하고 다음을 통해 선택할 수 있습니다: providerId이는 멀티 테넌트 B2B 앱이 각 고객의 자체 IdP를 사용할 수 있게 하는 방식입니다. 또한 플러그인은 리프레시 (SocialLogin.refresh), 로그아웃 및 id_token_hint을 지원하며,SocialLoginAuthConnectAuth Connect에서 마이그레이션하는 팀을 위한 Ionic Auth Connect 호환 래퍼 ( Auth0, Okta, Entra ID. 프로토콜 배경 지식은 다음을 참고하세요: Capacitor 앱에 OAuth2를 구현하는 5단계.
호스팅 백엔드: Firebase 및 Supabase
데이터가 이미 Firebase 또는 Supabase에 있다면, 해당 인증을 사용하세요. 그래야 추가 작업 없이 보안 규칙과 RLS(Row-Level Security)가 바로 적용됩니다.
- Supabase: Social Login 플러그인으로 ID 토큰을 가져온 뒤
supabase.auth.signInWithIdToken({ provider: 'google', token })를 호출합니다. Supabase가 이를 검증하고 세션을 반환합니다. 전체 설정 방법: Capacitor Social Login을 사용하는 Supabase. - Firebase: 다음 중 하나를 사용하세요
@capgo/capacitor-firebase-authentication, 이는 네이티브 Firebase Auth SDK를 래핑하거나, Social Login으로 ID token을 가져온 뒤 JS SDK에서 Firebase credential을 생성하는 방식입니다. 자세한 내용은 Firebase Google 로그인 문서에서 확인하세요.
자체 호스팅: Better Auth
Better Auth는 사용자를 자체 데이터베이스에 저장하는 오픈 소스 TypeScript 인증 라이브러리입니다. WKWebView에서는 크로스 사이트 쿠키가 불안정하므로, 앱 오리진을 신뢰하고 bearer token을 사용하도록 전환하면 Capacitor에서도 작동합니다. Social login은 네이티브 플러그인과 ID token 핸드오프를 통해 이루어집니다. 단계별 절차는 다음과 같습니다: Capacitor 앱에서 Better Auth를 사용하는 방법.
Passkeys
Passkeys는 비밀번호 대신 iCloud Keychain 또는 Google Password Manager에 저장된 키 쌍을 사용합니다. 이를 위해서는 앱이 웹사이트 도메인과 연결되어야 하며 (apple-app-site-association iOS의 경우, assetlinks.json Android의 경우) WebAuthn 서버가 필요합니다.
@capgo/capacitor-passkey 표준 WebAuthn code를 유지할 수 있습니다. capacitor.config.ts에서 relying party를 구성하고, 시작 시 shim을 한 번 호출한 뒤, navigator.credentials.create() 및 .get() 다음과 같이 네이티브 passkey API로 라우팅됩니다:
import { CapacitorPasskey } from '@capgo/capacitor-passkey';
await CapacitorPasskey.autoShimWebAuthn();
// existing WebAuthn code keeps working
const assertion = await navigator.credentials.get({
publicKey: requestOptionsFromServer,
});
Better Auth와 같은 라이브러리나 Auth0, Okta 같은 호스팅 제공업체를 WebAuthn 서버로 사용할 수 있습니다. 자세한 내용은 passkey 플러그인 페이지를.
자체 백엔드를 이용한 이메일 및 비밀번호 방식
별도의 플러그인은 필요하지 않으며, 단순히 fetch입니다. 모든 작업은 서버에서 이루어집니다. Argon2id 또는 bcrypt를 사용한 해싱, 로그인 시도 횟수 제한(rate-limit), 인증 및 비밀번호 재설정 이메일 발송, 계정 삭제 지원 등이 포함됩니다. 재설정 및 인증 링크는 Universal Links나 App Links를 통해 앱 내에서 바로 열리도록 설정하여, 사용자의 휴대폰에서 올바른 위치로 연결되게 하세요. 이 모든 것을 직접 관리해야 할 특별한 이유가 없다면, Better Auth 같은 라이브러리나 호스팅 서비스를 사용하는 것이 리스크를 줄이면서 동일한 기능을 제공받는 방법입니다.
항상 서버에서 토큰을 검증하세요
클라이언트 측 체크는 우회될 수 있습니다. 어떤 제공업체를 사용하든, 세션을 생성하기 전에 서버에서 반드시 토큰을 검증해야 합니다:
import { createRemoteJWKSet, jwtVerify } from 'jose';
type IssuerConfig = {
jwks: ReturnType<typeof createRemoteJWKSet>;
issuer: string | string[];
audience: string[];
};
const issuers: Record<'google' | 'apple', IssuerConfig> = {
google: {
jwks: createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs')),
issuer: ['https://accounts.google.com', 'accounts.google.com'],
audience: ['WEB_CLIENT_ID.apps.googleusercontent.com', 'IOS_CLIENT_ID.apps.googleusercontent.com'],
},
apple: {
jwks: createRemoteJWKSet(new URL('https://appleid.apple.com/auth/keys')),
issuer: 'https://appleid.apple.com',
audience: ['com.example.app', 'com.example.app.signin'],
},
};
export async function verifyIdToken(provider: 'google' | 'apple', idToken: string) {
const cfg = issuers[provider];
const { payload } = await jwtVerify(idToken, cfg.jwks, {
issuer: cfg.issuer,
audience: cfg.audience,
});
return payload; // use payload.sub as the stable provider user ID
}
제공업체의 주요 사용자 sub (이메일 대신 제공업체 이름 포함). 제공업체에서 이메일이 인증되었다고 표시한 경우에만 (email_verified: true) 이메일로 계정을 연결하세요. Meta의 Limited Login 토큰에는 해당 claim이 포함되어 있지 않습니다.
세션을 안전하게 저장하세요
localStorage 또한 Capacitor Preferences는 일반 파일입니다. 장기 사용 토큰은 Keychain(iOS) 또는 Keystore로 암호화된 저장소(Android)에 보관하세요. @capgo/capacitor-native-biometric 다음과 같은 간단한 setData, getData 및 deleteData 방법이 있습니다:
bun add @capgo/capacitor-native-biometric
bunx cap sync
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
export const sessionStore = {
async save(token: string) {
await NativeBiometric.setData({ key: 'session.token', value: token });
},
async load(): Promise<string | null> {
try {
const { value } = await NativeBiometric.getData({ key: 'session.token' });
return value;
} catch {
return null;
}
},
async clear() {
await NativeBiometric.deleteData({ key: 'session.token' }).catch(() => {});
},
};
액세스 토큰은 유효 기간을 짧게 유지하고, 리프레시 토큰이나 서버 세션을 통해 갱신하세요. 저희의 보안 토큰 저장 가이드에서는 토큰 로테이션 및 취소에 대해 더 자세히 다룹니다.
생체 인식: 신원 확인이 아닌 잠금 해제 용도
Face ID와 지문은 휴대폰을 든 사람이 잠금을 해제할 수 있다는 것만 증명합니다. 이를 저장된 세션에 대한 접근 제어용으로 사용하세요:
import { AccessControl, NativeBiometric } from '@capgo/capacitor-native-biometric';
// after a real login, offer biometric unlock
await NativeBiometric.setData({
key: 'session.refresh',
value: refreshToken,
accessControl: AccessControl.BIOMETRY_ANY,
});
// next app start
const { value } = await NativeBiometric.getSecureData({
key: 'session.refresh',
reason: 'Unlock your account',
});
// exchange `value` with your server for a fresh access token
GCP는 accessControl 설정하면, 생체 인증에 성공한 후에만 OS가 값을 해제하므로 JavaScript가 아닌 Keychain이나 Keystore에 의해 보호가 강제됩니다. 서버 측 세션 만료 및 고위험 앱의 경우 루팅/탈옥 감지와 함께 사용하세요. 자세한 내용은 Capacitor 앱의 생체 인증 가이드에서 확인하세요.
방법 선택하기
| 만약 당신이... | 사용하세요: |
|---|---|
| iOS 및 Android용 소비자 앱 출시 | Social Login(Apple + Google) 및 이메일/passkeys |
| Microsoft 365를 사용하는 기업 대상 판매 | OAuth2 프로바이더를 통한 Entra ID |
| 고객사별로 다양한 엔터프라이즈 IdP 필요 | Auth0/Okta 브로커 또는 여러 OAuth2 프로바이더 providerId |
| 이미 Supabase 또는 Firebase 사용 중 | 네이티브 ID 토큰 로그인을 통한 인증 |
| 벤더 없이 직접 사용자 테이블 관리 | 더 나은 인증 방식 |
| 비밀번호 없는 인증을 원하시나요? | 이메일 매직 링크를 대체 수단으로 쓰는 Passkeys |
| 빠른 재로그인이 필요한가요? | 위 방식에 더해 저장된 세션을 생체 인식으로 잠금 해제 |
로그인에 영향을 주는 App Store 및 Play 규칙
- Apple 4.8 로그인 서비스: 서드파티 로그인을 기본 계정 시스템으로 쓰려면 이에 상응하는 개인정보 보호 중심 옵션이 필요합니다. Sign in with Apple이 이를 충족합니다.
- Apple 5.1.1(v): 계정 생성 시 앱 내 계정 삭제가 필요하며, Sign in with Apple 토큰은 취소해야 합니다. 참조: 계정 삭제 규정 준수.
- Google Play 계정 삭제: 계정이 있는 앱은 앱 내 및 Play Console에 등록된 웹 링크를 통해 계정 삭제 기능을 제공해야 합니다.
- 리뷰어 액세스 권한: 리뷰어에게 작동 가능한 데모 계정을 제공하세요. SSO 전용 로그인인 경우 테스트용 테넌트 사용자를 제공해야 합니다.
- Xcode 26: App Store Connect는 2026년 4월부터 Xcode 26 및 iOS 26 SDK로 빌드된 필수 빌드를 요구하므로, 새로운 인증 기능을 추가하여 릴리스를 제출하기 전에 업데이트를 완료하세요.
인증 변경 사항을 안전하게 배포하기
Auth bugs block every user, so you want to fix them fast. Native changes (entitlements, URL schemes, intent filters) need a store build. Everything in TypeScript, like provider config, scopes, error handling and UI, can be pushed with Capgo Live Update 사용하기 문제가 발생하면 즉시 롤백할 수 있습니다. Capgo 빌드 새로운 native 릴리스가 필요한 경우 클라우드에서 서명된 iOS 및 Android 바이너리를 생성합니다.
다음 단계
원하는 제공업체를 선택하고 해당 가이드를 따라주세요: