게시일: 2026년 10월 23일
Chrome 155 이상에서는 Digital Credentials API 발급이 기본적으로 사용 설정되므로 더 이상 오리진 트라이얼 토큰이나 플래그를 사용하여 사용할 필요가 없습니다. Digital Credentials API는 웹사이트를 사용자의 휴대기기에 설치된 월렛 앱에 연결하는 전송 계층입니다. 발급을 통해 발급기관은 새 사용자 인증 정보 (예: 디지털 ID)를 사용자의 월렛 앱에 직접 추가할 수 있습니다.
배경
디지털 신원 생태계는 발급기관, 인증자 (신뢰 당사자라고도 함), 소지자 (지갑 앱)의 세 당사자로 구성됩니다. 사용자는 발급기관의 웹사이트 또는 앱에서 발급을 시작하는 푸시 프로비저닝을 통해 디지털 신분증을 월렛 앱에 추가하는 경우가 많습니다. 예를 들어 대학교 웹사이트 (발급기관)에서 학생은 디지털 학생증을 원하는 월렛 앱(소지자)에 저장할 수 있습니다.
이 모델이 성공하려면 발급기관이 월렛 앱을 호출하여 신분증을 발급하는 안전하고 안정적인 방법이 필요합니다. 이 API가 출시되기 전에는 각 발급기관이 파트너십을 맺은 월렛 앱을 여는 자체적인 방법을 구축해야 했으며, 일반적으로 맞춤 URI 스킴을 사용했습니다. 발급기관은 각 월렛 앱에 대해 별도의 메커니즘을 유지해야 했으며 사용자는 월렛 앱을 선택하는 일관된 방법이 없었습니다.
디지털 사용자 인증 정보 발급 작동 방식
이 프로세스를 간소화하기 위해 Chrome은 W3C Digital Credentials API를 통한 발급을 지원합니다. 이는 Chrome 141에서 제공된 프레젠테이션 기능(디지털 사용자 인증 정보에서 요청된 선택적 데이터를 인증자와 공유)에 추가됩니다. 발급기관 웹사이트가 발급 요청이 포함된 digital 멤버와 함께 navigator.credentials.create()를 호출하면 Chrome은 요청을 운영체제 (Android의 경우 인증 관리자)에 전달합니다. 그러면 운영체제에 사용자가 월렛 앱을 선택하는 자체 인터페이스가 표시됩니다. 이 API는 검증 가능한 사용자 인증 정보 발급을 위한 OpenID (OpenID4VCI)와 같은 업계 표준 프로토콜과 호환됩니다.
발급은 Android의 동일 기기 흐름과 데스크톱에서 시작하여 Android 기기에서 완료되는 교차 기기 흐름의 두 가지 흐름을 지원합니다.
Android의 동일 기기 흐름
Android에서 Chrome은 발급 요청을 Android 인증 관리자에 전달하고 인증 관리자는 기기에 호환되는 월렛 앱 목록을 표시합니다. 사용자가 월렛을 선택하고 사용자 인증 정보 추가에 동의합니다. 그러면 월렛 앱은 OpenID4VCI와 같은 발급 프로토콜을 사용하여 발급기관에서 사용자 인증 정보를 가져옵니다.
데스크톱의 교차 기기 흐름
데스크톱에서 Chrome에 QR 코드가 표시됩니다. 사용자는 Android 기기로 코드를 스캔하고 이전 섹션에 설명된 대로 해당 기기에서 흐름을 완료합니다. 기기는 블루투스를 사용하여 서로 가까이 있는지 확인하므로 Chrome은 블루투스가 꺼져 있는 경우 사용자에게 블루투스를 사용 설정하라는 메시지를 표시합니다. Chrome은 Digital Credentials API 프레젠테이션에 동일한 교차 기기 메커니즘을 사용합니다.
API를 사용하여 발급 구현
발급 과정을 시작하려면 navigator.credentials.create()를 호출하고 digital 멤버 내에서 발급 요청을 전달합니다. 이 호출에는 일시적인 사용자 활성화가 필요하므로 클릭 핸들러 또는 다른 사용자 동작 핸들러에서 이 호출을 실행합니다. 다음 예시에서는 사전 승인된 코드 플로우와 함께 OpenID4VCI 사용자 인증 정보 제안을 사용합니다.
// Call this function from a user gesture handler, for example, a button's
// click handler. The pre-authorized code is a short-lived, single-use code
// that your server generates for the signed-in user.
async function issueDigitalCredential(preAuthorizedCode) {
// Check for Digital Credentials API and protocol support.
if (!window.DigitalCredential ||
!DigitalCredential.userAgentAllowsProtocol('openid4vci-v1')) {
// Provide an alternative issuance method.
showAlternativeIssuanceFlow();
return;
}
// Construct a credential offer according to the OpenID4VCI
// specification. This example uses the format that the
// demo CMWallet supports.
const credentialOffer = {
credential_issuer: 'https://issuer.example',
credential_configuration_ids: ['org.iso.18013.5.1.mDL'],
grants: {
'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
'pre-authorized_code': preAuthorizedCode
}
},
// Metadata that the wallet uses to request and display the credential.
credential_issuer_metadata: {
credential_issuer: 'https://issuer.example',
credential_endpoint: 'https://issuer.example/openid4vci/credential',
nonce_endpoint: 'https://issuer.example/openid4vci/nonce',
credential_configurations_supported: {
'org.iso.18013.5.1.mDL': {
format: 'mso_mdoc',
doctype: 'org.iso.18013.5.1.mDL',
// Bind the credential to a key that the wallet generates.
cryptographic_binding_methods_supported: ['cose_key'],
proof_types_supported: {
jwt: { proof_signing_alg_values_supported: ['ES256'] }
},
credential_metadata: {
display: [{ name: 'Mobile driving license', locale: 'en-US' }],
// The claims that this credential contains. For a full list of
// standard mDL claims, see the ISO/IEC 18013-5 specification.
claims: [{
path: ['org.iso.18013.5.1', 'family_name'],
display: [{ name: 'Family name', locale: 'en-US' }]
}
// ... Add any other claims for your credential here.
]
}
}
}
},
// Metadata of the authorization server that issues access tokens.
authorization_server_metadata: {
issuer: 'https://issuer.example',
token_endpoint: 'https://issuer.example/openid4vci/token'
}
};
try {
const credential = await navigator.credentials.create({
digital: {
requests: [{
// The protocol identifier for OpenID4VCI 1.0.
protocol: 'openid4vci-v1',
// Pass the offer as an object, not as a JSON string.
data: credentialOffer
}]
}
});
// The wallet handled the request. credential.data is an object that
// contains the wallet's protocol-specific response. It is not the issued
// credential, and it does not necessarily mean that issuance is complete.
// Your server knows when the wallet retrieves the credential from your
// credential endpoint.
// Implement your custom logic here as needed.
showIssuanceStatus(credential);
} catch (error) {
switch (error.name) {
case 'NotAllowedError':
// The user canceled the flow, no wallet can handle the request, or
// the call does not have transient user activation.
break;
case 'NotSupportedError':
// The browser does not support Digital Credentials API issuance.
// Fall back to your custom alternative flow logic.
showAlternativeIssuanceFlow();
break;
case 'TypeError':
// The request is malformed, for example, data is not an object.
console.error('Invalid issuance request:', error);
break;
default:
// For example, AbortError or NetworkError.
console.error('Credential issuance failed:', error);
}
}
}
const addButton = document.querySelector('#add-to-wallet');
addButton.addEventListener('click', () => {
// Your server adds a pre-authorized code for the signed-in user
// to the button's data-pre-authorized-code attribute.
issueDigitalCredential(addButton.dataset.preAuthorizedCode);
});
이 예에서 showAlternativeIssuanceFlow() 및 showIssuanceStatus()는 자체 앱 로직을 나타냅니다.
교차 출처 iframe에서 create()를 호출하려면 삽입 페이지에서 digital-credentials-create 권한 정책 기능을 허용해야 합니다. 그렇지 않으면 호출이 NotAllowedError로 거부됩니다.
<iframe src="https://issuer.example" allow="digital-credentials-create">
</iframe>
오리진 트라이얼 이후 변경사항
API 메서드와 매개변수는 오리진 트라이얼 이후 변경되지 않았습니다. 하지만 다음 변경사항은 Chrome 143과 Chrome 155 사이에 출시되었으며, 이 중 일부는 코드를 업데이트해야 할 수 있습니다. 다음 표에는 변경사항이 요약되어 있으며, 그 뒤에 나오는 섹션에서 각 변경사항을 설명합니다.
| 영역 | 오리진 트라이얼 (Chrome 143) | Chrome 155 |
|---|---|---|
| 가용성 | 오리진 트라이얼 토큰 또는 chrome://flags/#web-identity-digital-credentials-creation 플래그 |
기본적으로 사용 설정됨 |
| 사용자 활성화 | 필수이지만 사용되지 않음 | 각 create() 호출에 필요하며 사용됨 (Chrome 146) |
| 취소 오류 | NetworkError |
NotAllowedError (Chrome 148) |
| 프로토콜 식별자 | Chrome에서 운영체제에 식별자를 전달함 | 인식할 수 없는 식별자가 지원 중단 경고를 트리거함 (Chrome 151) |
| 불투명 출처 | 허용됨 | NotAllowedError로 차단됨 (Chrome 151) |
| 자동 테스트 | 기본 지원 없음 | Chrome DevTools 프로토콜의 가상 지갑 (Chrome 152) |
사용자 활성화가 사용됨
Chrome 146부터 digital 멤버가 있는 create() 호출은 사양에 따라 일시적인 사용자 활성화를 사용합니다. 오리진 트라이얼 중에 Chrome은 사용자 활성화를 확인했지만 사용하지는 않았습니다.
따라서 각 create() 호출에는 버튼 클릭과 같은 자체 사용자 동작이 필요합니다. 사용자 활성화가 없으면 프로미스가 NotAllowedError로 거부됩니다. 호출 후에는 window.open() 및 사용자 활성화가 필요한 다른 API에서 동일한 동작을 사용할 수 없습니다.
NotAllowedError(으)로 취소 거부
Chrome 148부터 사용자가 흐름을 취소하면(예: Android에서 월렛 선택기를 닫거나 데스크톱에서 QR 코드 대화상자를 닫는 경우) create()에서 반환하는 프로미스가 NotAllowedError와 함께 거부됩니다. 요청을 처리할 수 있는 월렛 앱이 없는 경우에도 프로미스가 NotAllowedError로 거부됩니다.
오리진 트라이얼 중에는 이러한 케이스가 NetworkError로 거부되었습니다.
코드가 NetworkError를 확인하여 취소를 감지하는 경우 대신 NotAllowedError를 확인하도록 업데이트합니다.
인식할 수 없는 프로토콜 식별자가 경고를 트리거함
이 사양은 OpenID4VCI 1.0의 단일 발급 프로토콜 식별자인 openid4vci-v1를 정의합니다. Chrome 155는 openid4vci-v1 및 openid4vci를 인식합니다. openid4vci는 사양에 포함되지 않으므로 openid4vci-v1를 사용하는 것이 좋습니다.
Chrome은 프로토콜 식별자가 있는 요청을 플랫폼에 전달합니다. 하지만 Chrome 151부터는 페이지가 인식할 수 없는 프로토콜 식별자로 create()를 호출하면 DevTools에 지원 중단 경고가 표시됩니다. Chrome은 인식할 수 없는 프로토콜 식별자가 있는 요청을 차단할 예정입니다. 상태는 Digital Credential API의 보다 구체적인 프로토콜 필터링 항목을 참고하세요.
이 동작을 테스트하려면 chrome://flags/#enable-experimental-web-platform-features를 사용 설정합니다. 이 플래그를 사용 설정하면 create()는 인식할 수 없는 프로토콜 식별자가 있는 요청을 무시하고 요청이 남아 있지 않으면 TypeError로 거부합니다. 또한 DigitalCredential.userAgentAllowsProtocol()는 인식된 프로토콜에 대해서만 true을 반환합니다. 플래그가 없으면 식별자의 형식이 올바른지만 확인합니다.
따라서 기능 감지를 위해 userAgentAllowsProtocol()에만 의존하지 마세요.
예시와 같이 NotSupportedError를 처리합니다.
불투명한 출처가 차단됨
Chrome 151부터 create()는 allow-same-origin 토큰이 없는 샌드박스 처리된 iframe과 같이 불투명 출처가 있는 문서가 호출할 때 NotAllowedError로 거부합니다.
오리진 트라이얼 기간 동안 Chrome은 이러한 호출을 차단하지 않았습니다.
가상 지갑으로 테스트
Chrome 152부터 Android 기기나 월렛 앱 없이 발급 흐름을 테스트할 수 있습니다. Chrome DevTools 프로토콜 (CDP)의 실험용 DigitalCredentials.setVirtualWalletBehavior 명령어는 사양에서 자동 테스트를 위해 정의하는 가상 월렛을 구현합니다. 가상 지갑은 제공된 데이터로 응답하거나, 사용자가 흐름을 취소한 것처럼 요청을 거부하거나, 타임아웃 및 중단을 테스트하기 위해 요청을 대기 상태로 둘 수 있습니다.
다음 Puppeteer 테스트는 가상 지갑이 openid4vci-v1 요청에 응답하도록 설정한 다음 발급을 시작하는 버튼을 클릭합니다.
const client = await page.createCDPSession();
await client.send('DigitalCredentials.setVirtualWalletBehavior', {
action: 'respond',
protocol: 'openid4vci-v1',
response: {}
});
// Clicking the button provides the user activation that create() requires.
await page.click('#add-to-wallet');
업계 도입 상황
Digital Credentials API 발급이 채택되고 있습니다. 예를 들어 독일 금융 기관인 Sparkasse는 Digital Credentials API를 사용하여 계정 소유자가 확인된 연령 사용자 인증 정보를 Google 월렛에 저장할 수 있도록 합니다. 그러면 계정 소유자는 이러한 사용자 인증 정보를 사용하여 온라인과 오프라인에서 연령을 인증할 수 있습니다.
사용해 보기
디지털 인증 API 발급을 테스트하기 전에 다음 사항을 확인하세요.
- Chrome 155 이상으로 업데이트합니다.
- Android 기기에 지원되는 월렛 앱(예: 데모 CMWallet)을 설치합니다.
- 두 기기 모두 블루투스가 사용 설정되어 있어야 합니다 (교차 기기 흐름만 해당).
교차 기기 흐름을 테스트하려면 다음 단계를 따르세요.
- 컴퓨터에서 데모 발급기관 사이트를 엽니다.
- 추가 버튼을 클릭합니다.
- Android 기기로 QR 코드를 스캔하고 화면에 표시된 안내를 따릅니다.
동일 기기 흐름을 테스트하려면 Android 기기의 Chrome에서 데모 발급기관 사이트를 엽니다.
개발자 리소스
웹사이트에 Digital Credentials API 발급을 통합하려면 다음 리소스를 사용하세요.
- W3C 웹 ID 및 사용자 인증 정보 채택 커뮤니티 그룹의 개발자 가이드를 읽어보세요.
- W3C 디지털 자격 증명 사양을 읽으십시오.
- 검증 가능한 사용자 인증 정보 발급을 위한 OpenID 1.0 사양을 읽어보세요.
- Android용 오픈소스 데모 월렛 앱인 데모 CMWallet의 소스 코드를 살펴보세요.
- Android 월렛 앱이 인증 관리자의 발급 요청을 처리하는 방법을 알아보세요.
- Android 기기에 설치된 데모 CMWallet으로 데모 발급기관 사이트를 사용해 보세요.
- 디지털 사용자 인증 정보 요청 생성자 데모를 살펴보고 요청 페이로드가 어떻게 생겼는지 확인하세요.
의견 공유
버그를 신고하거나 기능을 요청하려면 Chromium 버그 추적기에서 문제를 제출하세요.