Issuer-initiated provisioning allows users to start the provisioning process directly from your app or website. This flow is useful when you want to encourage users who are already engaged with your platform to add their verifiable digital credential to Google Wallet.
To initiate provisioning from your issuer app or website, you must construct a CredentialOffer and pass it to the platform-specific API.
Android
On Android, use CredentialManager to initiate the provisioning flow.
Implementation
The following example demonstrates how to construct the request and call createCredential in your ViewModel.
// HomeViewModel.kt
import androidx.credentials.CredentialManager
import androidx.credentials.CreateDigitalCredentialRequest
// ... other imports
class HomeViewModel : ViewModel() {
fun onAddToWalletClicked(activity: Activity) {
viewModelScope.launch {
// 1. Gather the necessary data (protocol, issuerId, authCode)
val currentProtocol = _uiState.value.protocol
val currentIssuerId = _uiState.value.issuerId
val currentAuthorizationCode = _uiState.value.authorizationCode
try {
// 2. Construct the Credential Offer JSON
// The structure matches the "digital" object in the Web API.
val credentialOfferJson = JSONObject().apply {
put("requests", JSONArray().apply {
put(JSONObject().apply {
put("protocol", currentProtocol)
put("data", JSONObject().apply {
put("credential_issuer", currentIssuerId)
put("credentials", JSONArray()) // Add specific credentials if needed
put("grants", JSONObject().apply {
put("authorization_code", JSONObject().apply {
put("issuer_state", currentAuthorizationCode)
})
})
})
})
})
}.toString()
// 3. Initiate the flow using CredentialManager
val response = CredentialManager.create(activity).createCredential(
activity,
CreateDigitalCredentialRequest(credentialOfferJson)
)
// Handle successful response
_uiState.update { it.copy(result = Result.Credential("Credential creation request sent")) }
} catch (e: Exception) {
// Handle errors (e.g., user cancellation, API errors)
Log.e("HomeViewModel", "Failed to create credential: $e")
_uiState.update { it.copy(result = Result.Error(e.message)) }
}
}
}
}
UI (Jetpack Compose)
Call the ViewModel method from your composable.
// HomeScreen.kt
@Composable
fun HomeScreen(viewModel: HomeViewModel) {
// ... UI setup ...
FloatingActionButton(onClick = {
val currentActivity = LocalActivity.current
// Pass the Activity context to CredentialManager
viewModel.onAddToWalletClicked(requireNotNull(currentActivity))
}) {
Text("Add to Wallet")
}
// ... Rest of UI ...
}
Web
On the web, you use the Digital Credentials API to initiate the flow.
Implementation
Use navigator.credentials.create() with the openid4vci1.0 protocol.
issueButton.addEventListener('click', async () => {
const protocol = 'openid4vci1.0';
const issuerId = 'https://credential-issuer.example.com';
const preAuthCode = '...'; // Your pre-authorized code
// Construct the request object matching the "digital" request structure
const request = {
"protocol": protocol,
"data": {
"credential_issuer": issuerId,
"credentials": [],
"grants": {
"authorization_code": {
"issuer_state": preAuthCode
}
}
}
};
try {
// Request the credential
await navigator.credentials.create({
digital: {
requests: [request]
}
});
console.log('Credential issued successfully!');
} catch (error) {
console.error('Credential request failed:', error);
}
});
Prerequisites
- A configured Google Wallet Issuer account.
Request Structure
The request structure for initiating provisioning is consistent across Android and Web.
{
"protocol": "<protocol>",
"data": {
"credential_issuer": "https://credential-issuer.example.com",
"credentials": [
// Optional: Specific credential types to request
],
"grants": {
"authorization_code": {
"issuer_state": "..." // Pre-authorized code or state
}
}
}
}
Sequence Diagram
The following diagram illustrates the flow for Issuer-initiated provisioning.
Configurable Values
protocol- example:
google-canonicaloropenid4vci1.0(coming soon)
- example:
credentialIssuer- example: "https://credential-issuer.example.com"
credentialConfigurationIds- example:
["eu.europa.ec.av.1"]
- example:
Next steps
Once the provisioning intent is launched, the flow merges with the standard device registration process.
Proceed to Device registration.