이 가이드에서는 WebView를 사용하는 Android 앱에서 인증 관리자를 사용하여 디지털 사용자 인증 정보를 요청하는 방법을 설명합니다. JavaScript 브리지를 사용하여 웹페이지와 앱 간의 통신을 원활하게 합니다.
개요
다음 단계에서는 WebView의 디지털 사용자 인증 정보 요청 수명 주기를 설명합니다.
- 요청 생성: WebView의 웹페이지가 앱 서버에 요청을 보내면 앱 서버는 OpenID4VP 요청 JSON을 생성하여 다시 전송합니다.
- 요청 시작: 웹페이지는
navigator.credentials.get({ digital: ...})를 사용하여 요청 JSON을 통해 디지털 사용자 인증 정보를 요청합니다. - 가로채기: 삽입된
bridge.js가 이 호출을 가로채고 특정 OpenID4VP 요청 JSON을 추출하여 Android 앱에 전달합니다. - Android 처리: Android 앱이 메시지를 수신하고
CredentialManager.getCredentialAPI를 호출합니다. - 사용자 선택: 인증 관리자는 사용자에게 사용자 인증 정보를 공유하라는 메시지를 표시하는 UI를 표시합니다. 사용자 인증 정보가 Android 앱에 반환됩니다.
- 브리지 응답: Android 앱은 브리지를 사용하여 웹페이지에 응답을 다시 전송합니다.
- 완료: 웹페이지에서 응답을 수신하고 처리합니다.
기본 요건
WebView에서 디지털 사용자 인증 정보용 JavaScript 브리지를 구현하기 전에 프로젝트가 다음 요구사항을 충족하는지 확인하세요.
- Android 버전 호환성: 앱이 Android 6.0(API 수준 23) 이상을 타겟팅해야 합니다.
- 호스트 앱 제어: 웹페이지를 로드하기 전에
WebMessageListener를 연결할 수 있도록WebView를 호스팅하는 Android 앱을 제어해야 합니다. - HTTPS 및 최상위 프레임: 사용자 인증 정보를 요청하는 웹페이지는
https://를 통해 제공되고 기본 프레임에서 실행되어야 합니다 (isMainFrame는true여야 함). 하위 프레임과 삽입된<iframe>요소는 지원되지 않습니다. - 실험용 API 선택: Digital Credentials API는 실험용이므로
GetDigitalCredentialOption를 사용하는 클래스나 메서드에@OptIn(ExperimentalDigitalCredentialApi::class)로 주석을 달아야 합니다.
종속 항목 추가
앱 모듈의 build.gradle.kts 파일에 인증 관리자 및 AndroidX WebKit 종속 항목을 추가합니다.
dependencies {
// Credential Manager dependencies
implementation("androidx.credentials:credentials:1.7.0-alpha03")
implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha03")
// AndroidX WebKit (required for WebMessageListener bridge)
implementation("androidx.webkit:webkit:1.14.0")
}
WebView에서 디지털 사용자 인증 정보 구현
WebView에 JavaScript를 추가하여 사용자 인증 정보 요청을 만들고 bridge.js로 가로채야 합니다.
JavaScript 요청 만들기
WebView에서 실행되는 JavaScript와 Android 코드 간에 통신하려면 JavaScript 코드를 WebView에 삽입하세요.
// Example digital credential request to request verified email
const request = {
"providers": [
{
"protocol": "openid4vp",
"request": JSON.stringify({
"requests": [
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "n-0S6_W_2F", // Generate dynamically
"dcql_query": {
"credentials": [
{
"id": "user_info_query",
"format": "dc+sd-jwt",
"meta": {
"vct_values": ["UserInfoCredential"]
},
"claims": [
{"path": ["email"]},
{"path": ["email_verified"]},
{"path": ["name"]}
]
}
]
}
}
}
]
})
}
]
};
try {
const credential = await navigator.credentials.get({
digital: request
});
// Process response
} catch (err) {
// Handle error
}
JavaScript 브리지 만들기
bridge.js라는 파일을 만들거나 evaluateJavascript를 사용하여 이 코드를 삽입합니다.
이 스크립트는 navigator.credentials.get를 재정의하여 요청을 가로채고 내부 OpenID4VP JSON 문자열을 Android 앱으로 전송합니다.
(function() {
if (!navigator.credentials) {
navigator.credentials = {};
}
const originalGet = navigator.credentials.get;
// Overrides navigator.credentials.get() to intercept W3C Digital Credential requests.
navigator.credentials.get = async function(options) {
if (options && options.digital) {
if (window.DigitalCredentialBridge) {
return new Promise((resolve, reject) => {
const digital = options.digital;
if (digital.providers && digital.providers.length > 0) {
const provider = digital.providers[0];
if (provider.protocol === "openid4vp") {
const requestJson = provider.request;
const message = {
type: "get",
request: requestJson
};
console.log("Sending message to native bridge:", JSON.stringify(message));
// Send the request string to native Android code
window.DigitalCredentialBridge.postMessage(JSON.stringify(message));
// Receive the response sent from native code using replyProxy.postMessage()
window.DigitalCredentialBridge.onmessage = function(event) {
console.log("Received response from native bridge:", event.data);
try {
const response = JSON.parse(event.data);
if (response.error) {
reject(new Error(response.error));
} else {
resolve(response);
}
} catch (e) {
// If it's not JSON, just resolve with the raw data
resolve(event.data);
}
};
} else {
reject(new Error("Unsupported protocol: " + provider.protocol));
}
} else {
reject(new Error("No providers specified in digital credential request"));
}
});
} else {
console.warn("DigitalCredentialBridge not found, falling back to original");
}
}
if (originalGet) {
return originalGet.call(navigator.credentials, options);
} else {
throw new Error("navigator.credentials.get is not supported and no bridge available");
}
};
})();
Android에서 디지털 사용자 인증 정보 구현
bridge.js에서 전송된 사용자 인증 정보 요청을 처리하도록 Android 앱을 구성합니다.
Android 리스너 만들기
WebViewCompat.WebMessageListener를 구현하는 DigitalCredentialWebListener 클래스를 설정합니다. 이 클래스는 자바스크립트와 네이티브 Android 코드 간의 양방향 메시지를 처리하여 isMainFrame 및 sourceOrigin에 대한 보안 검사를 시행하고 중복 요청을 방지합니다.
인증 관리자 핸들러 구현
getCredential를 호출하여 기기에서 디지털 사용자 인증 정보를 요청합니다.
@OptIn(ExperimentalDigitalCredentialApi::class)
class CredentialManagerHandler(private val context: Context) {
private val credentialManager = CredentialManager.create(context)
suspend fun getDigitalCredential(requestJson: String): String {
val digitalCredentialOption = GetDigitalCredentialOption(requestJson)
val getCredRequest = GetCredentialRequest(listOf(digitalCredentialOption))
return try {
val result = credentialManager.getCredential(context, getCredRequest)
val credential = result.credential
if (credential is DigitalCredential) {
credential.credentialJson
} else {
"{\"error\": \"Unexpected credential type\"}"
}
} catch (e: GetCredentialException) {
"{\"error\": \"${e.message}\"}"
} catch (e: Exception) {
"{\"error\": \"Unknown exception\"}"
}
}
companion object {
private const val TAG = "CredManHandler"
}
}
활동에 프레임워크 적용
Activity 또는 View에서 WebMessageListener 및 관련 함수를 등록합니다.
// Inside your Activity or View setup
AndroidView(
factory = { context ->
val handler = CredentialManagerHandler(context)
WebView(context).apply {
settings.javaScriptEnabled = true
// Register the bridge before loading URL
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
val listener = DigitalCredentialWebListener(coroutineScope, credentialManagerHandler)
WebViewCompat.addWebMessageListener(
webView,
INTERFACE_NAME,
setOf("*"),
listener
)
loadUrl("https://your-secure-domain.com/login")
}
},
)
흐름 테스트 중
흐름을 테스트하여 WebView와 Android 앱 간의 통신을 확인합니다. 프로세스 중에 WebView에 사용자 인증 정보 선택기가 표시되고 완료되면 선택한 사용자 인증 정보가 WebView로 반환됩니다.