사용자가 패스키로 인증하려면 먼저 앱에서 계정의 패스키를 등록하거나 만들어야 합니다.
패스키를 만들려면 앱 서버에서 패스키를 만드는 데 필요한 세부정보를 가져온 다음 공개 키와 비공개 키 쌍을 반환하는 Credential Manager API를 호출합니다. 반환된 비공개 키는 Google 비밀번호 관리자와 같은 사용자 인증 정보 제공자에 패스키로 저장됩니다. 공개 키는 앱 서버에 저장됩니다.
기본 요건
디지털 애셋 링크를 설정하고 Android 9 (API 수준 28) 이상을 실행하는 기기를 타겟팅해야 합니다.
개요
이 가이드에서는 신뢰 당사자 클라이언트 앱에서 패스키를 만들기 위해 필요한 변경사항에 중점을 두고 신뢰 당사자 앱 서버 구현에 관한 간략한 개요를 제공합니다. 서버 측 통합에 대해 자세히 알아보려면 서버 측 패스키 등록을 참고하세요.
- 앱에 종속 항목 추가: 필요한 인증 관리자 라이브러리를 추가합니다.
- 인증 관리자 인스턴스화: 인증 관리자 인스턴스를 만듭니다.
- 앱 서버에서 사용자 인증 정보 생성 옵션 가져오기: 앱 서버에서 클라이언트 앱에 앱, 사용자,
challenge및 기타 필드에 관한 정보와 같이 패스키를 만드는 데 필요한 세부정보를 전송합니다. - 패스키 요청: 앱에서 앱 서버로부터 수신한 세부정보를 사용하여
GetPublicKeyCredentialOption객체를 만들고 이 객체를 사용하여credentialManager.getCredential()메서드를 호출하여 패스키를 만듭니다. - 패스키 생성 응답 처리: 클라이언트 앱에서 사용자 인증 정보를 수신하면 공개 키를 인코딩하고 직렬화한 후 앱 서버로 전송해야 합니다. 또한 패스키 생성 시 발생할 수 있는 각 예외를 처리해야 합니다.
- 서버에서 공개 키 확인 및 저장: 서버 측 단계를 완료하여 사용자 인증 정보의 출처를 확인한 다음 공개 키를 저장합니다.
- 사용자에게 알림: 사용자에게 패스키가 생성되었다고 알립니다.
1. 앱에 종속 항목 추가
앱 모듈의 build.gradle 파일에 다음 종속 항목을 추가합니다.
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.6.0-beta03") implementation("androidx.credentials:credentials-play-services-auth:1.6.0-beta03") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.6.0-beta03" implementation "androidx.credentials:credentials-play-services-auth:1.6.0-beta03" }
2. 인증 관리자 인스턴스화
앱 또는 활동 컨텍스트를 사용하여 CredentialManager 객체를 만듭니다.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
3. 앱 서버에서 사용자 인증 정보 생성 옵션 가져오기
사용자가 '패스키 만들기' 버튼을 클릭하거나 새 사용자가 가입할 때 앱에서 앱 서버로 패스키 등록 절차를 시작하는 데 필요한 정보를 가져오도록 요청합니다.
앱 서버에서 FIDO 규격 라이브러리를 사용하여 사용자, 앱, 추가 구성 속성에 관한 정보 등 패스키를 만드는 데 필요한 정보를 클라이언트 앱에 전송합니다. 자세한 내용은 서버 측 패스키 등록을 참고하세요.
클라이언트 앱에서 앱 서버가 보낸 공개 키 생성 옵션을 디코딩합니다. 일반적으로 JSON 형식으로 표시됩니다. 웹 클라이언트에서 이 디코딩이 어떻게 이루어지는지 자세히 알아보려면 인코딩 및 디코딩을 참고하세요. Android 클라이언트 앱의 경우 디코딩을 별도로 처리해야 합니다.
다음 스니펫은 앱 서버에서 전송한 공개 키 생성 옵션의 구조를 보여줍니다.
{
"challenge": "<base64url-encoded challenge>",
"rp": {
"name": "<relying party name>",
"id": "<relying party host name>"
},
"user": {
"id": "<base64url-encoded user ID>",
"name": "<user name>",
"displayName": "<user display name>"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"attestation": "none",
"excludeCredentials": [
{
"id": "<base64url-encoded credential ID to exclude>",
"type": "public-key"
}
],
"authenticatorSelection": {
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
공개 키 생성 옵션의 주요 필드는 다음과 같습니다.
challenge: 재생 공격을 방지하는 데 사용되는 서버 생성 임의 문자열입니다.rp: 앱에 관한 세부정보입니다.rp.name: 앱의 이름입니다.rp.id: 앱의 도메인 또는 하위 도메인입니다.
user: 사용자에 관한 세부정보입니다.id: 사용자의 고유 ID입니다. 이 값에는 이메일 주소나 사용자 이름 등 개인 식별 정보가 포함되면 안 됩니다. 임의의 16바이트 값을 사용할 수 있습니다.name: 사용자가 인식할 수 있는 계정의 고유 식별자입니다(예: 이메일 주소 또는 사용자 이름). 이름은 계정 선택기에 표시됩니다. 사용자 이름을 사용하는 경우 비밀번호 인증에서와 동일한 값을 사용하세요.displayName: 계정 선택기에 표시하기 위한 계정의 선택적 사용자 친화적 이름입니다.
authenticatorSelection: 인증에 사용될 기기에 관한 세부정보입니다.authenticatorAttachment: 선호하는 인증기를 나타냅니다. 가능한 값은 다음과 같습니다. -platform: 이 값은 지문 센서와 같이 사용자 기기에 내장된 인증자에 사용됩니다. -cross-platform: 이 값은 보안 키와 같은 로밍 기기에 사용됩니다. 일반적으로 패스키 컨텍스트에서는 사용되지 않습니다. - 지정되지 않음 (권장): 이 값을 지정하지 않으면 사용자가 선호하는 기기에서 패스키를 생성할 수 있습니다. 대부분의 경우 매개변수를 지정하지 않는 것이 가장 좋습니다.requireResidentKey: 패스키를 만들려면 이Boolean필드의 값을true로 설정합니다.residentKey: 패스키를 만들려면 값을required로 설정합니다.userVerification: 패스키 등록 중에 사용자 확인 요구사항을 지정하는 데 사용됩니다. 가능한 값은 다음과 같습니다. -preferred: 사용자 인증으로 인해 보호보다 마찰이 더 많이 발생하는 환경과 같이 보호보다 사용자 환경을 우선시하는 경우 이 값을 사용합니다. -required: 기기에서 사용할 수 있는 사용자 인증 방법을 호출해야 하는 경우 이 값을 사용합니다. -discouraged: 사용자 확인 방법을 사용하지 않는 것이 권장되는 경우 이 값을 사용합니다.userVerification에 대해 자세히 알아보려면 userVerification 심층 분석을 참고하세요.
excludeCredentials: 동일한 사용자 인증 정보 제공업체에 이미 패스키가 있는 경우 중복 패스키가 생성되지 않도록 배열에 사용자 인증 정보 ID를 나열합니다.
4. 패스키 요청
서버 측 공개 키 생성 옵션을 파싱한 후 이러한 옵션을 CreatePublicKeyCredentialRequest 객체로 래핑하고 createCredential()를 호출하여 패스키를 만듭니다.
createPublicKeyCredentialRequest에는 다음이 포함됩니다.
requestJson: 앱 서버에서 전송한 사용자 인증 정보 생성 옵션입니다.preferImmediatelyAvailableCredentials: 보안 키 또는 하이브리드 키 흐름의 사용자 인증 정보 대신 로컬에서 제공되는 사용자 인증 정보 또는 사용자 인증 정보 제공업체 동기화 사용자 인증 정보만 사용하여 요청을 처리할지 여부를 정의하는 불리언 필드입니다(선택사항). 가능한 사용법은 다음과 같습니다.false(기본값): 인증 관리자 호출이 명시적인 사용자 작업에 의해 트리거된 경우 이 값을 사용합니다.true: 앱을 처음 여는 경우와 같이 인증 관리자가 기회주의적으로 호출되는 경우 이 값을 사용합니다.
값을true로 설정했지만 즉시 사용 가능한 사용자 인증 정보가 없으면 인증 관리자에 어떤 UI도 표시되지 않고 요청이 즉시 실패하여 get 요청에 NoCredentialException이, create 요청에CreateCredentialNoCreateOptionException이 반환됩니다.
origin: 이 필드는 Android 앱에 대해 자동으로 설정됩니다.origin를 설정해야 하는 브라우저 및 유사한 권한이 있는 앱의 경우 권한이 있는 앱의 다른 당사자를 대신하여 인증 관리자 호출을 참고하세요.isConditional: 기본값이false인 선택적 필드입니다. 이 값을true로 설정하고 사용자에게 패스키가 없는 경우 사용자가 저장된 비밀번호로 다음에 로그인할 때 패스키가 자동으로 생성됩니다. 패스키는 사용자의 사용자 인증 정보 제공업체에 저장됩니다. 조건부 생성 기능을 사용하려면androidx.credentials의 최신 버전이 필요합니다.
createCredential() 함수를 호출하면 사용자에게 패스키를 사용하고 저장할 인증 제공자와 계정을 선택하라는 메시지를 표시하는 인증 관리자의 기본 제공 하단 시트 UI가 실행됩니다. 하지만 isConditional이 true로 설정되면 하단 시트 UI가 표시되지 않고 패스키가 자동으로 생성됩니다.
5. 응답 처리
사용자가 기기의 화면 잠금을 사용하여 인증되면 패스키가 생성되어 사용자가 선택한 사용자 인증 정보 제공업체에 저장됩니다.
createCredential()를 성공적으로 호출한 후의 응답은 PublicKeyCredential 객체입니다.
PublicKeyCredential는 다음과 같습니다.
{
"id": "<identifier>",
"type": "public-key",
"rawId": "<identifier>",
"response": {
"clientDataJSON": "<ArrayBuffer encoded object with the origin and signed challenge>",
"attestationObject": "<ArrayBuffer encoded object with the public key and other information.>"
},
"authenticatorAttachment": "platform"
}
클라이언트 앱에서 객체를 직렬화하고 앱 서버로 전송합니다.
다음 스니펫과 같이 실패를 처리하는 코드를 추가합니다.
fun handleFailure(e: CreateCredentialException) {
when (e) {
is CreatePublicKeyCredentialDomException -> {
// Handle the passkey DOM errors thrown according to the
// WebAuthn spec.
}
is CreateCredentialCancellationException -> {
// The user intentionally canceled the operation and chose not
// to register the credential.
}
is CreateCredentialInterruptedException -> {
// Retry-able error. Consider retrying the call.
}
is CreateCredentialProviderConfigurationException -> {
// Your app is missing the provider configuration dependency.
// Most likely, you're missing the
// "credentials-play-services-auth" module.
}
is CreateCredentialCustomException -> {
// You have encountered an error from a 3rd-party SDK. If you
// make the API call with a request object that's a subclass of
// CreateCustomCredentialRequest using a 3rd-party SDK, then you
// should check for any custom exception type constants within
// that SDK to match with e.type. Otherwise, drop or log the
// exception.
}
else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
}
}
6. 앱 서버에서 공개 키 확인 및 저장
앱 서버에서 공개 키 사용자 인증 정보를 확인한 다음 공개 키를 저장해야 합니다.
공개 키 인증서의 출처를 확인하려면 승인된 앱의 허용 목록과 비교하세요. 키에 인식할 수 없는 출처가 있으면 거부합니다.
앱의 SHA 256 지문을 가져오려면 다음 단계를 따르세요.
터미널에서 다음 명령어를 실행하여 출시 앱의 서명 인증서를 출력합니다.
keytool -list -keystore <path-to-apk-signing-keystore>대답에서 서명 인증서의 SHA 256 지문을 확인합니다(
Certificate fingerprints block:SHA256).base64url 인코딩으로 SHA256 지문을 인코딩합니다. 이 Python 예시는 지문을 올바르게 인코딩하는 방법을 보여줍니다.
import binascii import base64 fingerprint = '<SHA256 finerprint>' # your app's SHA256 fingerprint print(base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))이전 단계의 출력 시작 부분에
android:apk-key-hash:을 추가하여 다음과 비슷한 결과를 얻습니다.android:apk-key-hash:<encoded SHA 256 fingerprint>결과는 앱 서버의 허용된 출처와 일치해야 합니다. 디버깅 및 출시용 인증서와 같은 서명 인증서가 여러 개 있거나 앱이 여러 개 있는 경우 이 프로세스를 반복하고 이러한 모든 출처를 앱 서버에서 유효한 것으로 허용합니다.
7. 사용자에게 알림
패스키가 성공적으로 생성되면 사용자에게 패스키에 관해 알리고 사용자 인증 정보 제공업체 앱 또는 앱 설정 내에서 패스키를 관리할 수 있다고 안내합니다. 맞춤 대화상자, 알림 또는 스낵바를 사용하여 사용자에게 알립니다. 악성 주체에 의한 예기치 않은 패스키 생성에는 즉각적인 보안 알림이 필요하므로 이러한 인앱 방법을 이메일과 같은 외부 커뮤니케이션으로 보완하는 것이 좋습니다.
사용자 환경 향상하기
인증 관리자를 사용한 가입을 구현하는 동안 사용자 환경을 개선하려면 사용자 인증 정보 복원 및 자동 완성 대화상자 억제를 위한 기능을 추가하는 것이 좋습니다.
새 기기에서 사용자 인증 정보를 복원하는 기능 추가
사용자가 새 기기에서 계정에 원활하게 로그인할 수 있도록 사용자 인증 정보 복원 기능을 구현하세요. BackupAgent로 복원 사용자 인증 정보를 추가하면 사용자가 새 기기에서 복원된 앱을 열 때 로그인되어 즉시 앱을 사용할 수 있습니다.
사용자 인증 정보 필드에서 자동 완성 억제 (선택사항)
사용자가 인증을 위해 사용자 인증 정보 관리자의 하단 시트 UI를 사용할 것으로 예상되는 앱 화면의 경우 사용자 이름 및 비밀번호 필드에 isCredential 속성을 추가합니다. 이렇게 하면 자동 완성 대화상자 (FillDialog 및 SaveDialog)가 인증 관리자의 하단 시트 UI와 겹치지 않습니다.
isCredential 속성은 Android 14 이상에서 지원됩니다.
다음 예에서는 앱의 관련 뷰에 있는 관련 사용자 이름 및 비밀번호 필드에 isCredential 속성을 추가하는 방법을 보여줍니다.
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isCredential="true" />