Digital credential verification within Android apps can be used to authenticate and authorize a user's identity (such as a government ID), properties about that user (such as a driver's license, academic degree, or attributes such as age or address), or other scenarios where a credential needs to be issued and verified to assert the authenticity of an entity.
Digital Credentials is a public W3C incubator standard that
specifies how to access a user's verifiable digital credentials from a digital
wallet, and is implemented for web use cases with the W3C Credential Management
API. On Android, Credential Manager's
DigitalCredential
API is used for verifying digital credentials.
Implementation
To verify digital credentials in your Android project, do the following:
- Add dependencies to your app's build script and initialize a
CredentialManager
class. - Construct a digital credential request and use it to initialize a
DigitalCredentialOption
, followed by building theGetCredentialRequest
. - Launch the
getCredential
flow with the constructed request to receive a successfulGetCredentialResponse
or handle any exceptions that may occur. Upon successful retrieval, validate the response.
Add dependencies and initialize
Add the following dependencies to your Gradle build script:
dependencies {
implementation("androidx.credentials:credentials:1.5.0-beta01")
implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}
Next, Initialize an instance of the CredentialManager
class.
val credentialManager = CredentialManager.create(context)
Construct a digital credential request
Construct a digital credential request and use it to initialize a
DigitalCredentialOption
.
// The request in the JSON format to conform with
// the JSON-ified Digital Credentials API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
GetDigitalCredentialOption(requestJson = requestJson)
// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
listOf(digitalCredentialOption)
)
Get the credential
Launch the getCredential
flow with the constructed request. You will receive
either a successful GetCredentialResponse
, or a GetCredentialException
if
the request fails.
The getCredential
flow triggers Android system dialogs to present the user's
available credential options and collect their selection. Next, the wallet app
that contains the chosen credential option will display UIs to collect consent
and perform actions needed to generate a digital credential response.
coroutineScope.launch {
try {
val result = credentialManager.getCredential(
context = activityContext,
request = getCredRequest
)
verifyResult(result)
} catch (e : GetCredentialException) {
handleFailure(e)
}
}
// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
val credential = result.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}")
}
}
}
// Handle failure.
fun handleFailure(e: GetCredentialException) {
when (e) {
is GetCredentialCancellationException -> {
// The user intentionally canceled the operation and chose not
// to share the credential.
}
is GetCredentialInterruptedException -> {
// Retry-able error. Consider retrying the call.
}
is NoCredentialException -> {
// No credential was available.
}
is CreateCredentialUnknownException -> {
// An unknown, usually unexpected, error has occurred. Check the
// message error for any additional debugging information.
}
is CreateCredentialCustomException -> {
// You have encountered a custom error thrown by the wallet.
// If you made the API call with a request object that's a
// subclass of CreateCustomCredentialRequest using a 3rd-party SDK,
// then you should check for any custom exception type constants
// within that SDK to match with e.type. Otherwise, drop or log the
// exception.
}
else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
}
}