인증 관리자 소지자 API를 사용하면 Android 소지자('지갑'이라고도 함) 앱에서 디지털 사용자 인증 정보를 관리하고 검증 도구에 제시할 수 있습니다. 이 가이드에서는 인증 관리자에 소지자 앱의 사용자 인증 정보를 등록하는 방법과 사용자가 사용자 인증 정보를 선택할 때 프레젠테이션 요청을 처리하는 방법을 설명합니다. Holder API는 Android 6 (API 수준 23) 이상에서 지원됩니다.
종속 항목 추가
Credential Manager Holder API를 사용하려면 앱 모듈의 build.gradle 스크립트에 다음 종속 항목을 추가하세요.
dependencies {
// Used to implement credential registries
implementation "androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha05"
implementation "androidx.credentials.registry:registry-digitalcredentials-openid:1.0.0-alpha05"
implementation "androidx.credentials.registry:registry-digitalcredentials-sdjwtvc:1.0.0-alpha05"
implementation "androidx.credentials.registry:registry-provider:1.0.0-alpha05"
implementation "androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha05"
}
사용자 인증 정보 등록
먼저 OpenId4VpRegistry 요청을 등록하는 데 사용할 RegistryManager 인스턴스를 만듭니다.
// Create the registry manager
val registryManager = RegistryManager.create(context)
OpenId4VpRegistry 요청 빌드
검증자의 OpenID4VP 요청을 처리하려면 OpenId4VpRegistry를 등록해야 합니다. 월렛 사용자 인증 정보 (예: sdJwtsFromStorage 및 mdocsFromStorage)가 로드된 로컬 데이터 유형이 있다고 가정하면 이를 Jetpack DigitalCredentialEntry에 상응하는 SdJwtEntry 및 MdocEntry로 변환해야 합니다.
레지스트리에 SD-JWT 추가
각 로컬 SD-JWT 사용자 인증 정보를 레지스트리의 SdJwtEntry에 매핑합니다.
fun mapToSdJwtEntries(sdJwtsFromStorage: List<StoredSdJwtEntry>): List<SdJwtEntry> {
val list = mutableListOf<SdJwtEntry>()
for (sdJwt in sdJwtsFromStorage) {
list.add(
SdJwtEntry(
verifiableCredentialType = sdJwt.getVCT(),
claims = sdJwt.getClaimsList(),
entryDisplayPropertySet = sdJwt.toDisplayProperties(),
id = sdJwt.getId() // Make sure this cannot be readily guessed
)
)
}
return list
}
레지스트리에 mdoc 추가
로컬 mdoc 사용자 인증 정보를 Jetpack 유형 MdocEntry에 매핑합니다.
fun mapToMdocEntries(mdocsFromStorage: List<StoredMdocEntry>): List<MdocEntry> {
val list = mutableListOf<MdocEntry>()
for (mdoc in mdocsFromStorage) {
list.add(
MdocEntry(
docType = mdoc.retrieveDocType(),
fields = mdoc.getFields(),
entryDisplayPropertySet = mdoc.toDisplayProperties(),
id = mdoc.getId() // Make sure this cannot be readily guessed
)
)
}
return list
}
코드에 관한 핵심 사항
- 암호화된 ID:
ID필드를 구성하는 한 가지 방법은 암호화된 사용자 인증 정보 식별자를 등록하여 나만 값을 복호화할 수 있도록 하는 것입니다. - 현지화: 두 형식의 UI 표시 필드를 현지화해야 합니다.
요청 결합 및 등록
변환된 항목을 결합하고 RegistryManager에 요청을 등록합니다.
val credentialEntries =
mapToSdJwtEntries(sdJwtsFromStorage) + mapToMdocEntries(mdocsFromStorage)
val openidRegistryRequest = OpenId4VpRegistry(
credentialEntries = credentialEntries,
id = "my-wallet-openid-registry-v1" // A stable, unique ID to identify your registry record.
)
선택사항: 인라인 발급
사용자에게 요청된 유형의 기존 사용자 인증 정보가 없는 경우 소지자에게 사용자 인증 정보를 추가하는 옵션을 표시할 수 있습니다. 이렇게 하려면 지원되는 사용자 인증 정보가 포함된 inlineIssuanceEntries 목록을 OpenId4VpRegistry에 추가합니다.
val openidRegistryRequest = OpenId4VpRegistry(
credentialEntries = credentialEntries,
inlineIssuanceEntries = emptyList(),
listOf(
MdocInlineIssuanceEntry(
id = "Issuance",
display = InlineIssuanceEntry.InlineIssuanceDisplayProperties(
subtitle = "Mobile Drivers License, State Id, and Others",
),
supportedMdocs = setOf(
MdocInlineIssuanceEntry.SupportedMdoc(
"eu.europa.ec.eudi.pid.1"
),
MdocInlineIssuanceEntry.SupportedMdoc(
"org.iso.18013.5.1.mDL1"
),
)
),
SdJwtInlineIssuanceEntry(
id = "sd-jwt-issuance",
display = InlineIssuanceEntry.InlineIssuanceDisplayProperties(
subtitle = "Mobile Drivers License, State Id, and Others",
),
supportedSdJwts = setOf(
SdJwtInlineIssuanceEntry.SupportedSdJwt("urn:openid:interop:id:1")
)
)
),
id = "my-wallet-openid-registry-v1"
)
CredentialManager에 사용자 인증 정보를 등록합니다.
try {
val response = registryManager.registerCredentials(openidRegistryRequest)
} catch (e: Exception) {
// Handle failure
}
앱 메타데이터 관리
소지자 앱이 CredentialManager에 등록하는 메타데이터에는 다음과 같은 속성이 있습니다.
- 지속성: 정보가 로컬에 저장되어 재부팅 후에도 유지됩니다.
- 사일로화된 저장소: 각 앱의 레지스트리 레코드는 별도로 저장되므로 한 앱이 다른 앱의 레지스트리 레코드를 변경할 수 없습니다.
- 키가 지정된 업데이트: 각 앱의 레지스트리 레코드는
id로 키가 지정되므로 레코드를 다시 식별, 업데이트 또는 삭제할 수 있습니다. - 메타데이터 업데이트: 앱이 변경되거나 처음 로드될 때마다 유지된 메타데이터를 업데이트하는 것이 좋습니다. 동일한
id에서 레지스트리가 여러 번 호출되면 최신 호출이 이전의 모든 레코드를 덮어씁니다. 업데이트하려면 먼저 이전 레코드를 삭제할 필요 없이 다시 등록합니다.
선택사항: 일치자 만들기
매처는 인증 관리자가 샌드박스에서 실행하여 수신되는 검증 도구 요청에 대해 등록된 사용자 인증 정보를 필터링하는 Wasm 바이너리입니다.
- 기본 매처:
OpenId4VpRegistry클래스를 인스턴스화하면 기본OpenId4VP매처(OpenId4VpDefaults.DEFAULT_MATCHER)가 자동으로 포함됩니다. 모든 표준 OpenID4VP 사용 사례의 경우 라이브러리가 일치를 처리합니다. - 커스텀 일치자: 자체 일치 로직이 필요한 비표준 프로토콜을 지원하는 경우에만 커스텀 일치자를 구현합니다.
선택한 사용자 인증 정보 처리
사용자가 사용자 인증 정보를 선택하면 소지자 앱에서 요청을 처리해야 합니다.
androidx.credentials.registry.provider.action.GET_CREDENTIAL 인텐트 필터를 수신 대기하는 Activity를 정의해야 합니다.
샘플 월렛에서 이 절차를 보여줍니다.
인텐트는 인증 도구 요청 및 호출 출처를 사용하여 활동을 실행합니다. 이는 PendingIntentHandler.retrieveProviderGetCredentialRequest 함수를 사용하여 추출합니다. 그러면 인증자 요청과 관련된 모든 정보가 포함된 ProviderGetCredentialRequest가 반환됩니다. 세 가지 주요 구성요소가 있습니다.
- 호출 앱: 요청을 한 앱으로,
getCallingAppInfo를 사용하여 가져올 수 있습니다. 이를 사용하여 검증 도구 ID를 확인할 수 있습니다. - 선택한 사용자 인증 정보: 사용자가 선택한 후보에 관한 정보로,
selectedCredentialSet extension method를 통해 검색됩니다. 이는 등록한 사용자 인증 정보 ID와 일치합니다. - 특정 요청:
getCredentialOptions메서드에서 가져온, 인증자가 보낸 특정 요청입니다. 디지털 사용자 인증 정보 요청 흐름의 경우 이 목록에서 단일GetDigitalCredentialOption를 찾을 수 있습니다.
다음 코드를 사용하여 프레젠테이션 요청을 처리할 수 있습니다.
request.credentialOptions.forEach { option ->
if (option is GetDigitalCredentialOption) {
Log.i(TAG, "Got DC request: ${option.requestJson}")
processRequest(option.requestJson)
}
}
이에 관한 예는 샘플 월렛에서 확인할 수 있습니다.
확인자 신원 확인
먼저 인텐트에서 ProviderGetCredentialRequest를 추출합니다.
val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
권한이 있는 앱 확인: 권한이 있는 앱 (예: 웹브라우저)은 출처 매개변수를 설정하여 다른 인증자를 대신하여 호출할 수 있습니다. 이 출처를 가져오려면 권한이 있고 신뢰할 수 있는 호출자 목록 (JSON 형식의 허용 목록)을 CallingAppInfo getOrigin API에 전달합니다.
val origin = request?.callingAppInfo?.getOrigin(
privilegedAppsJson // Your allow list JSON
)
origin이 비어 있지 않은 경우: packageName 및 인증서 지문이 허용 목록의 앱과 일치하면 출처가 반환됩니다. 이를 권한이 있는 호출로 간주하고 호출 앱의 서명을 사용하여 출처를 계산하는 대신 OpenID4VP 응답에 이 출처를 설정합니다.
origin가 비어 있는 경우: 인증자 요청이 Android 앱에서 전송됩니다. OpenID4VP 응답에 매핑된 출처는 android:apk-key-hash:<encoded SHA 256 fingerprint>로 계산되어야 합니다.
val appSigningInfo =
request?.callingAppInfo?.signingInfoCompat?.signingCertificateHistory?.get(0)?.toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val certHash =
Base64.encodeToString(md.digest(appSigningInfo), Base64.NO_WRAP or Base64.NO_PADDING)
val computedOrigin = "android:apk-key-hash:$certHash"
소지자 UI 렌더링
사용자 인증 정보가 선택되면 소지자 앱이 호출되어 앱의 UI를 통해 사용자를 안내합니다. 이 워크플로를 처리하는 방법에는 두 가지가 있습니다.
- 사용자 인증 정보를 공개하는 데 추가 사용자 인증이 필요한 경우 BiometricPrompt API를 사용합니다. 이는 샘플에 나와 있습니다.
- 그렇지 않으면 많은 월렛이 데이터를 호출 앱에 즉시 다시 전달하는 빈 활동을 렌더링하여 자동 반환을 선택합니다. 이렇게 하면 사용자 클릭이 최소화되고 더 원활한 환경이 제공됩니다.
사용자 인증 정보 응답 반환
소지자 앱이 결과를 다시 전송할 준비가 되면 사용자 인증 정보 응답으로 활동을 완료합니다.
PendingIntentHandler.setGetCredentialResponse(
resultData,
GetCredentialResponse(DigitalCredential(response.responseJson))
)
setResult(RESULT_OK, resultData)
finish()
예외가 있는 경우 다음과 같이 사용자 인증 정보 예외를 전송할 수 있습니다.
PendingIntentHandler.setGetCredentialException(
resultData,
GetCredentialUnknownException() // Configure the proper exception
)
setResult(RESULT_OK, resultData)
finish()
컨텍스트에서 사용자 인증 정보 응답을 반환하는 전체 예는 샘플 앱을 참고하세요.