本指南詳細說明如何使用 DigitalCredential API 取得使用者的已驗證電話號碼。這個程序包含兩個步驟:
- 要求
TS.43 token
:用戶端應用程式 (「驗證者」) 向使用者裝置要求臨時 TS.43 權杖。TS.43 token
是電信業者核發的憑證,代表使用者的身分。 - 以權杖換取電話號碼:應用程式後端會與匯總器或電信業者交換
TS.43 token
,取得使用者已驗證的電話號碼。
必要條件
如要使用 DigitalCredential API 實作電話號碼驗證,您需要擁有匯總工具帳戶。整合器會與貨運公司互動,並為應用程式提供必要的 API 介面,通常是可計費的雲端 API 端點。
您也需要在 Gradle 建構指令碼中加入下列依附元件:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.6.0-alpha05") implementation("androidx.credentials:credentials-play-services-auth:1.6.0-alpha05") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.6.0-alpha05" implementation "androidx.credentials:credentials-play-services-auth:1.6.0-alpha05" }
實作
端對端程序通常會依下列步驟進行:
- 向匯總器要求 DCQL (數位憑證查詢語言) 參數:呼叫一或多個匯總器,並要求一組 DCQL 參數。您可以使用 DCQL,指定從各個匯總工具取得的確切數位憑證。
建立 OpenID4VP 要求:從應用程式後端建立 OpenID4VP 要求,同時加入匯總工具的 DCQL 參數。然後將 OpenID4VP 要求傳送至用戶端應用程式。
呼叫 Credential Manager API:在用戶端應用程式中,使用 Credential Manager API 將 OpenID4VP 要求傳送至作業系統。您會收到 OpenID4VP 回應物件,其中包含
TS.43 Digital Credential
。這項憑證經過加密,只能由相關聯的匯總工具解密。收到貨運公司權杖後,請將用戶端應用程式的回應傳送至應用程式後端。驗證回應:在應用程式的後端,驗證 OpenID4VP 回應。
換取電話號碼:從應用程式後端將
TS.43 Digital Credential
傳送給匯總工具。彙整器會驗證憑證,並傳回已驗證的電話號碼。
向匯總工具索取 DCQL 參數
從應用程式後端向匯總工具傳送要求,索取數位憑證查詢語言 (DCQL) 憑證物件。請務必在要求中提供 Nonce 和要求 ID。匯總工具會傳回 DCQL 憑證物件,結構類似於下列內容:
{
// The credential ID is mapped to the request ID that is sent in your request to the aggregator.
"id": "aggregator1",
"format": "dc-authorization+sd-jwt",
"meta": {
"vct_values": [
"number-verification/device-phone-number/ts43"
],
"credential_authorization_jwt": "..."
},
"claims": [
{
"path": ["subscription_hint"],
"values": [1]
},
{
"path": ["phone_number_hint"],
"values": ["+14155552671"]
}
]
}
建立 OpenID4VP 要求
首先,請從應用程式後端建立 dcql_query
物件,方法是將 DCQL 憑證物件放在 dcql_query
物件中巢狀結構的 credentials
陣列,如下列範例所示:
"dcql_query": {
"credentials": [
"id": "aggregator1",
"format": "dc-authorization+sd-jwt",
"meta": {
"vct_values": [
"number-verification/device-phone-number/ts43"
],
"credential_authorization_jwt": "..."
},
"claims": [
{
"path": ["subscription_hint"],
"values": [1]
},
{
"path": ["phone_number_hint"],
"values": ["+14155552671"]
}
]
]
}
然後,建立結構如下的 OpenID4VP 要求:
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "...",
"dcql_query": { ... }
}
}
protocol
:必須設為openid4vp-v1-unsigned
,才能提出電話號碼驗證要求。response_type
和response_mode
:常數,分別表示具有固定值vp_token
和dc_api
的要求格式。nonce
:後端為每個要求產生的專屬值。匯總器 DCQL 憑證物件中的隨機碼必須與這個隨機碼相符。dcql_query
:在這種情況下,請使用dcql_query
指定要求TS.43 Digital Credential
。您也可以在這裡申請其他數位憑證。
接著,將 OpenID4VP 要求包裝在 DigitalCredential API 要求物件中,並傳送至用戶端應用程式。
{
"requests":
[
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "...",
"dcql_query": { ... }
}
}
]
}
下列程式碼片段示範如何產生 DigitalCredential API 要求:
def GenerateDCRequest():
credentials = []
aggregator1_dcql = call_aggregator_endpoint(nonce, "aggregator1", additional_params)
credentials.append(aggregator1_dcql) # You can optionally work with multiple
# aggregators, or request other types of credentials
val dc_request =
{
"requests":
[
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "...",
"dcql_query": {"credentials": credentials}
}
}
]
}
return dc_request
呼叫 Credential Manager API
在用戶端應用程式中,使用應用程式後端提供的 DigitalCredential API 要求,呼叫 Credential Manager API。
val requestJson = generateTs43DigitalCredentialRequestFromServer()
val digiCredOption = GetDigitalCredentialOption(requestJson = requestJson)
val getCredRequest = GetCredentialRequest(
listOf(digiCredOption)
)
coroutineScope.launch {
try {
val response = credentialManager.getCredential(
context = activityContext,
request = getCredRequest
)
val credential = response.credential
when (credential) {
is DigitalCredential -> {
val responseJson = credential.credentialJson
validateResponseOnServer(responseJson)
}
else -> {
// Catch any unrecognized credential type here.
Log.e(TAG, "Unexpected type of credential ${credential.type}")
}
}
} catch (e : GetCredentialException) {
// If user cancels the operation, the feature isn't available, or the
// SIM doesn't support the feature, a GetCredentialCancellationException
// will be returned. Otherwise, a GetCredentialUnsupportedException will
// be returned with details in the exception message.
handleFailure(e)
}
}
DigitalCredential API 回應包含 OpenID4VP 回應。DigitalCredential
結果中的典型憑證 JSON 如下:
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"vp_token": {
"aggregator1": ["eyJhbGciOiAiRVMy..."] # The encrypted TS.43 Digital
# Credential in an array structure.
}
}
}
從用戶端應用程式將 DigitalCredential API 回應傳送回後端伺服器,以便驗證並與匯總工具交換經過驗證的電話號碼。
驗證數位憑證回應
以下範例說明如何在應用程式後端剖析回應,並執行驗證步驟:
def processDigitalCredentialsResponse(response):
# Step 1: Parse out the TS.43 Digital Credential from the response
openId4VpResponse = response['data']
ts43_digital_credential = response['vp_token']["aggregator1"][0]
# Step 2: Perform response validation
verifyResponse(ts43_digital_credential)
def verifyResponse(ts43_digital_credential):
# The returned ts43_digital_credential is an SD-JWT-based Verifiable Credentials
# (SD-JWT VC) as defined in this IETF spec. The section 3.4 of the specification
# outlines how to validate the credential. At a high level, the steps involves
# validating (1) the nonce in the response credential matches the one in the
# request, (2) the integrity of the credential by checking the credential is
# signed by the trusted issuer Android Telephony, and (3) other validity
# properties associated with this credential, such as issue time and expiration
# time
# In most cases, you can use an SD-JWT VC library to perform these validations.
# Some aggregators may also perform the validation logic for you. Check with your
# aggregator to decide the exact scope of the validation required.
電話號碼的交換碼
從應用程式的後端將經過驗證的 TS.43 Digital Credential
傳送至匯總工具的端點,驗證憑證並接收已驗證的電話號碼。
def processDigitalCredentialsResponse(response):
# ... prior steps
# Step 3: Call aggregator endpoint to exchange the verified phone number
callAggregatorPnvEndpoint(ts43_digital_credential)