인증 연동
AuthMethodButton·LoginMethods로 만든 로그인 UI를 실제 인증에 연결하는 방법 — 패스키 생애주기(등록→인증→복구), 웹 vs 네이티브 OS 호출 맵, SSO·서드파티 흐름, 배선 레시피. DS는 UI를, 제품은 인증 의식을 담당합니다.
마지막 업데이트 2026-06-22
로그인 패턴이 **화면(UI)**을 정의한다면, 이 문서는 그 버튼들을 실제 인증에 연결하는 방법을 정리합니다 — 패스키 등록부터 로그인·복구까지의 프로세스와, OS·디바이스별 호출 차이입니다.
경계 — 무엇을 DS가 주고, 무엇을 제품이 배선하나#
인증 의식(WebAuthn 호출·자격증명 저장·세션)은 보안 민감 + 백엔드 결합이라 디자인 시스템이 코드로 담지 않습니다. DS는 UI·UX·접근성을, 제품(또는 인증 SDK)이 인증 로직을 책임집니다.
| WIZ DS가 제공 | 제품이 배선 |
|---|---|
AuthMethodButton·LoginMethods·ProviderButton (UI) | WebAuthn 등록/인증 의식(navigator.credentials) |
| 상태(로딩·실패·잠금)·동등 노출(parity)·a11y | RP 서버(challenge 발급·공개키 저장·서명 검증) |
| 로그인 화면 패턴·conditional UI 가이드 | 세션·토큰·계정 복구·다중기기 정책 |
즉 버튼은 “트리거”이고, 그
onClick에 제품의 인증 호출을 연결합니다. DS는 어떤 인증 백엔드(SimpleWebAuthn·Auth0·Hanko·자체 OIDC)도 강제하지 않습니다.
패스키 생애주기 (프로세스)#
패스키는 등록(Registration) 없이는 인증(Authentication) 할 수 없습니다(닭-달걀). 그래서 최초 진입은 항상 다른 1차 방식을 거칩니다.
- 1차 로그인 — 비밀번호·조직 SSO·이메일 매직링크 중 하나로 최초 인증.
- 패스키 등록 — 1차 로그인 직후 권유하거나 계정 설정에서. 기기의 생체(Face ID·지문·Windows Hello·PIN)로 키쌍 생성, 공개키만 서버에 저장.
- 이후 인증 — 다음 로그인부터 패스키로. 가능하면 **conditional UI(자동완성)**로 이메일 필드에서 바로 제안.
- 크로스디바이스 — 등록 안 된 기기에선 QR/hybrid로 휴대폰 패스키 사용.
- 폴백 — 패스키 불가 환경에선 비밀번호·SSO·매직링크로 회귀(항상 제공).
- 복구 — 모든 패스키 분실 시 이메일 복구·신뢰 기기·관리자 재설정.
등록 진입점을 설정 + 1차 로그인 직후 권유 두 곳에 두는 것이 채택률을 높이는 표준 패턴입니다(Passkey UX).
등록 의식 (Registration)#
// 1) 서버: 등록 옵션 + 일회성 challenge 발급 (RP ID = 도메인)
const options = await fetch('/auth/passkey/register/options').then((r) => r.json());
// 2) 클라이언트: 브라우저+OS가 생체 프롬프트를 띄우고 키쌍 생성
// (직접 navigator.credentials.create 대신 라이브러리 권장 — 아래 '배선' 참고)
import { startRegistration } from '@simplewebauthn/browser';
const credential = await startRegistration(options);
// 3) 서버: 공개키·credential_id·signature_count·AAGUID 저장 (개인키는 저장하지 않음)
await fetch('/auth/passkey/register/verify', {
method: 'POST',
body: JSON.stringify(credential),
});
2025 신규 — Conditional Create: 비밀번호로 로그인할 때 백그라운드로 패스키를 자동 생성(자동 업그레이드)할 수 있습니다. 별도 버튼 없이 패스키 보급을 늘립니다.
인증 의식 (Authentication)#
// 명시 버튼(AuthMethodButton method="passkey")의 onClick에 연결
import { startAuthentication } from '@simplewebauthn/browser';
async function onPasskey() {
const options = await fetch('/auth/passkey/login/options').then((r) => r.json());
const assertion = await startAuthentication(options);
await fetch('/auth/passkey/login/verify', { method: 'POST', body: JSON.stringify(assertion) });
}
<AuthMethodButton method="passkey" onClick={onPasskey} />
conditional UI (자동완성) — 권장 1차 경로#
명시 버튼보다 이메일 필드의 자동완성에 패스키를 띄우는 것이 모던 권장입니다(비밀번호 아래 3차 버튼은 안티패턴 — 사용자가 비번을 먼저 채움).
// 이메일 인풋에 webauthn 토큰 추가 (autocomplete 의 마지막 토큰이어야 함)
<Input id="login-email" autoComplete="username webauthn" />
// 폼 마운트 시 conditional mediation 으로 자동완성 제안 활성화
import { startAuthentication } from '@simplewebauthn/browser';
const supportsAutofill = await window.PublicKeyCredential?.isConditionalMediationAvailable?.();
if (supportsAutofill) {
const options = await fetch('/auth/passkey/login/options').then((r) => r.json());
startAuthentication(options, /* useBrowserAutofill */ true); // 백그라운드 대기
}
→ 이때 AuthMethodButton method="passkey"는 보완/명시 경로(자동완성 미지원·식별자 우선 흐름)로 둡니다.
플랫폼 호출 맵 — 웹 vs 네이티브#
핵심: 웹은 OS가 달라도 호출이 동일합니다(브라우저+OS가 차이를 흡수). 네이티브 앱만 OS별 API가 갈립니다.
| 타겟 | 호출 API | OS별 분기 | 동기화 제공자 |
|---|---|---|---|
웹 (@wds/ui-web) | navigator.credentials (WebAuthn) | 불필요 — Win Hello·Touch ID·Android 생체를 OS가 처리 | iCloud Keychain · Google PM · Windows Hello (OS 자동) |
iOS 네이티브 (wiz_ui) | ASAuthorization / AuthenticationServices (iOS 16+) | iOS 전용 | iCloud Keychain |
Android 네이티브 (wiz_ui) | Credential Manager (Android 14+) | Android 전용 | Google Password Manager |
그래서 웹 제품은 OS별 코드를 거의 쓰지 않습니다 — 하나의 WebAuthn 연동이 Windows·macOS·iOS·Android 브라우저에서 동작합니다. per-OS 작업은 Flutter 네이티브 앱에서만 필요하며, 이는 후속
wiz_ui패리티 트랙에서 다룹니다. 출처: W3C WebAuthn L2 · passkeys.dev · Android Credential Manager · web.dev conditional UI
네이티브 패스키 호출 레시피 (Flutter)#
네이티브 앱에서 WizAuthMethodButton 은 UI 트리거일 뿐입니다 — 버튼의 onPressed 안에서 플랫폼 채널이나 패스키 패키지(passkeys 등)로 OS 인증 의식을 호출합니다. 등록/인증 의식·서버 검증은 제품(또는 인증 SDK)이 배선합니다. DS는 화면을, 제품은 인증을 책임지는 경계는 네이티브에서도 동일합니다.
Android — Credential Manager (androidx.credentials):
// 등록: 새 패스키 생성 (서버가 내려준 옵션 JSON 사용)
val request = CreatePublicKeyCredentialRequest(requestJson = optionsJson)
val response = credentialManager.createCredential(activity, request)
// 인증: 기존 패스키로 로그인
val getRequest = GetCredentialRequest(listOf(GetPublicKeyCredentialOption(requestJson = challengeJson)))
val credential = credentialManager.getCredential(activity, getRequest)
iOS — AuthenticationServices (ASAuthorization):
// 플랫폼 패스키 프로바이더 (relyingPartyIdentifier = 도메인)
let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: "example.com")
let request = provider.createCredentialRegistrationRequest(challenge: challenge, name: userName, userID: userID)
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.performRequests()
// Flutter UI: 버튼은 트리거, onPressed 가 플랫폼 채널/패키지로 위 의식을 호출
WizAuthMethodButton(
method: WizAuthMethod.passkey,
onPressed: () => authService.signInWithPasskey(),
)
직접 구현 대신
passkeys같은 패키지가 Android Credential Manager·iOS ASAuthorization 을 한 API로 감쌉니다. 서버 의식(옵션 생성·검증)은 웹과 동일하게 재사용합니다(RP ID = 도메인). 출처: Android — Credential Manager · Apple — AuthenticationServices
조직 SSO (OIDC / SAML)#
// 식별자 우선: 이메일 도메인으로 조직 IdP 를 디스커버리한 뒤 리다이렉트
<AuthMethodButton method="sso" onClick={() => location.assign('/auth/sso/start?email=' + email)} />
서버는 이메일 도메인 → 조직 IdP 매핑으로 OIDC/SAML 리다이렉트를 시작합니다. 식별자 우선 흐름과 자연스럽게 맞물립니다(이메일 입력 → 방식 라우팅).
서드파티 (Apple · Google)#
브랜드 고정 버튼은 ProviderButton을 쓰고(색·로고·라벨이 IdP 가이드라인에 고정), 각 IdP의 OAuth/OIDC SDK로 연결합니다. WDS 테마로 바꾸지 않습니다.
배선 레시피 · 라이브러리#
직접 WebAuthn 구현은 까다롭고 보안 함정이 많습니다 — 검증된 라이브러리/매니지드 서비스를 권합니다:
- 셀프호스팅:
@simplewebauthn/server+@simplewebauthn/browser(RP 의식·검증). - 매니지드: Auth0 · Hanko · Corbado · Stytch (패스키 + 폴백 + 복구 포함).
- 서버 설정 핵심: RP ID = 도메인(서브도메인 주의), origin 검증,
signature_count로 복제 탐지.
폴백 · 복구 · 다중기기#
- 폴백 필수 — 패스키 불가(구형 브라우저·기기) 시 비밀번호/매직링크/SSO로 회귀.
- 복구 — 모든 패스키 분실 대비: 신뢰 기기·이메일 복구 링크·관리자 재설정 중 최소 하나.
- 다중기기 — 동기화 제공자(iCloud/Google)가 같은 계정 기기에 자동 전파 + 크로스디바이스(QR)로 비동기 기기 커버.
적용 체크리스트#
- RP ID·origin 서버 설정
- 등록 진입점 2곳(설정 + 1차 로그인 직후 권유)
- conditional UI(이메일 필드
autocomplete="username webauthn") - 명시 패스키 버튼(
AuthMethodButton method="passkey")을 보완으로 - 폴백 방식(비번·매직링크·SSO) 항상 제공
- 복구 경로 1개 이상
-
LoginMethods로 방식 동등 노출(prominence parity) — 한 방식을 3차로 묻지 않기 - 접근성: 44px 타깃·라벨·포커스 순서(접근성)
참고#
W3C WebAuthn Level 2 · passkeys.dev · FIDO Alliance · web.dev — 패스키 자동완성 · Apple — AuthenticationServices · Android — Credential Manager · SimpleWebAuthn