skill: add Android skills

This commit is contained in:
2026-07-14 10:34:54 +08:00
parent e260c02629
commit ab82fb7cf0
182 changed files with 62992 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
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 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](https://www.w3.org/TR/credential-management-1/). On
Android, Credential Manager's [`DigitalCredential`](https://developer.android.com/reference/kotlin/androidx/credentials/DigitalCredential) API is used for
verifying digital credentials.
### Android version compatibility
The Verifier API is supported on Android 9 (API level 28) and higher.
### Implementation
To verify digital credentials in your Android project, do the following:
1. Add dependencies to your app's build script and initialize a `CredentialManager` class.
2. Construct a digital credential request and use it to initialize a `DigitalCredentialOption`, followed by building the `GetCredentialRequest`.
3. Launch the `getCredential` flow with the constructed request to receive a successful `GetCredentialResponse` 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.6.0-beta01")
implementation("androidx.credentials:credentials-play-services-auth:1.6.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 Credential Manager - Verifier 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)
)
Here is an example of an OpenId4Vp request. A full reference can be found at
this [website](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
{
"requests": [
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "OD8eP8BYfr0zyhgq4QCVEGN3m7C1Ht_No9H5fG5KJFk",
"dcql_query": {
"credentials": [
{
"id": "cred1",
"format": "mso_mdoc",
"meta": {
"doctype_value": "org.iso.18013.5.1.mDL"
},
"claims": [
{
"path": [
"org.iso.18013.5.1",
"family_name"
]
},
{
"path": [
"org.iso.18013.5.1",
"given_name"
]
},
{
"path": [
"org.iso.18013.5.1",
"age_over_21"
]
}
]
}
]
}
}
}
]
}
#### 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}")
}
}

View File

@@ -0,0 +1,293 @@
> [!IMPORTANT]
> **Important:** We released an agent skill to help you implement email verification with the Digital Credentials API. Try out the skill from the [Android skills
> repository](https://github.com/android/skills).
This guide describes how to implement verified email retrieval using the
[Digital Credentials Verifier API](https://developer.android.com/identity/digital-credentials/credential-verifier) through an [OpenID for Verifiable
Presentations (OpenID4VP)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) request.
## Add dependencies
In your app's `build.gradle` file, add the following dependencies for Credential
Manager:
### Kotlin
```kotlin
dependencies {
implementation("androidx.credentials:credentials:1.7.0-alpha02")
implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02")
}
```
### Groovy
```groovy
dependencies {
implementation "androidx.credentials:credentials:1.7.0-alpha02"
implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02"
}
```
## Initialize Credential Manager
Use your app or activity context to create a `CredentialManager` object.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
## Construct the Digital Credential request
To request a verified email, construct a [`GetCredentialRequest`](https://developer.android.com/reference/android/credentials/GetCredentialRequest)
containing a [`GetDigitalCredentialOption`](https://developer.android.com/reference/androidx/credentials/GetDigitalCredentialOption). This option requires a
`requestJson` string formatted as an OpenID for Verifiable Presentations
(OpenID4VP) request.
The OpenID4VP request JSON must follow a specific structure. The current
providers support a JSON structure with an outer `"digital": {"requests":
[...]}` wrapper.
val nonce = generateSecureRandomNonce()
// This request follows the OpenID4VP spec
val openId4vpRequest = """
{
"requests": [
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "$nonce",
"dcql_query": {
"credentials": [
{
"id": "user_info_query",
"format": "dc+sd-jwt",
"meta": {
"vct_values": ["UserInfoCredential"]
},
"claims": [
{"path": ["email"]},
{"path": ["name"]},
{"path": ["given_name"]},
{"path": ["family_name"]},
{"path": ["picture"]},
{"path": ["hd"]},
{"path": ["email_verified"]}
]
}
]
}
}
}
]
}
"""
val getDigitalCredentialOption = GetDigitalCredentialOption(requestJson = openId4vpRequest)
val request = GetCredentialRequest(listOf(getDigitalCredentialOption))
The request contains the following key information:
- **DCQL query** : The `dcql_query` specifies the credential type and the
claims being requested (`email_verified`). You can request other claims to
determine the level of verification. A few possible claims are as follows:
- `email_verified`: In the response, this is a Boolean that indicates whether the email is verified.
- `hd` (hosted domain): In the response, this is empty.
> [!NOTE]
> **Note:** If `email_verified` is `true` and `hd` is empty in the response, it implies that the account is an authorized Google Account. Google does not issue [verifiable credentials](https://developer.android.com/identity/digital-credentials#verifiable-credentials) for Google Workspace Accounts. However, the `hd` field is present in verifiable credentials issued for non-workspace accounts. You are encouraged to implement handling this field to future-proof your app. If the email is non-@gmail.com, Google verified this email when the Google Account was created, but there is no freshness claim. Therefore, for non-Google emails, you should consider an additional challenge, such as an OTP, to verify the user. To understand the schema of the credential and the specific rules for validating fields like `email_verified`, refer to the [Google
> Identity guides](https://developers.google.com/identity/gsi/web/guides/verify-google-id-token).
- **nonce**: A unique, cryptographically secure random value is generated for
each request. This is critical for security, as it prevents replay attacks.
- `UserInfoCredential`: This value implies a specific type of digital
credential that contains user attributes. Including this in the request is
pivotal to distinguish the email verification use case.
Next, wrap the `openId4vpRequest` JSON in a `GetDigitalCredentialOption`, create
a `GetCredentialRequest`, and call `getCredential()`.
> [!NOTE]
> **Note:** The `hd` and `email_verified` fields are hidden from users in Credential Manager's built-in UI. You cannot make a request with only these hidden fields- in case of such requests, the response is the [`GetCredentialCancellationException`](https://developer.android.com/reference/kotlin/androidx/credentials/exceptions/GetCredentialCancellationException).
## Present the request to the user
Present the user with the request, using the Credential Manager built-in UI.
try {
// Requesting Digital Credential from user...
val result = credentialManager.getCredential(activity, request)
when (val credential = result.credential) {
is DigitalCredential -> {
val responseJsonString = credential.credentialJson
// Successfully received digital credential response.
// Next, parse this response and send it to your server.
// ...
}
else -> {
// handle Unexpected State() - Up to the developer
}
}
} catch (e: Exception) {
// handle exceptions - Up to the developer
}
> [!NOTE]
> **Note:** There is no equivalent of Sign in with Google's `preferImmediatelyAvailableCredentials` for Digital Credentials. If no verifiable credential is found (for example, no eligible account on device), the user will be shown a "No options available" or similar system screen.
## Parse the response on the client
After receiving the response, you can perform a preliminary parse on the client.
This is useful for immediately updating the UI, for example, by showing the
user's name.
> [!IMPORTANT]
> **Important:** This step is not for validation. Full cryptographic verification must be performed on your server.
The following code extracts the raw [Selective Disclosure JWT
(SD-JWT)](https://datatracker.ietf.org/doc/rfc9901/) and uses a helper to decode its claims.
// 1. Parse the outer JSON wrapper to get the `vp_token`
val responseData = JSONObject(responseJsonString)
val vpToken = responseData.getJSONObject("vp_token")
// 2. Extract the raw SD-JWT string
val credentialId = vpToken.keys().next()
val rawSdJwt = vpToken.getJSONArray(credentialId).getString(0)
// 3. Use your parser to get the verified claims
// Server-side validation/parsing is highly recommended.
// Assumes a local parser like the one in our SdJwtParser.kt sample
val claims = SdJwtParser.parse(rawSdJwt)
Log.d("TAG", "Parsed Claims: ${claims.toString(2)}")
// 4. Create your VerifiedUserInfo object with REAL data
val userInfo = VerifiedUserInfo(
email = claims.getString("email"),
displayName = claims.optString("name", claims.getString("email"))
)
## Handle the response
The Credential Manager API will return a [`DigitalCredential`](https://developer.android.com/reference/androidx/credentials/DigitalCredential)
response.
The following is an example of what the raw `responseJsonString` looks like, and
what the claims look like after parsing the inner SD-JWT where you get
additional metadata as well along with verified email:
/*
// Example of the raw JSON response from credential.credentialJson:
{
"vp_token": {
// This key matches the 'id' you set in your dcql_query
"user_info_query": [
// The SD-JWT string (Issuer JWT ~ Disclosures ~ Key Binding JWT)
"eyJhbGciOiJ...~WyI...IiwgImVtYWlsIiwgInVzZXJAZXhhbXBsZS5jb20iXQ~...~eyJhbGciOiJ..."
]
}
}
// Example of the parsed and verified claims from the SD-JWT on your server:
{
"cnf": {
"jwk": {..}
},
"exp": 1775688222,
"iat": 1775083422,
"iss": "https://verifiablecredentials-pa.googleapis.com",
"vct": "UserInfoCredential",
"email": "jane.doe.246745@gmail.com",
"email_verified": true,
"given_name": "Jane",
"family_name": "Doe",
"name": "Jane Doe",
"picture": "http://example.com/janedoe/me.jpg",
"hd": ""
}
*/
> [!IMPORTANT]
> **Important:** We highly recommend that after receiving the verified email, you trigger Credential Manager's [passkey creation](https://developer.android.com/identity/credential-manager/passkeys/create-passkeys).
## Server-side validation for account creation
Since the retrieved email is cryptographically verified, you can omit the email
OTP verification step, significantly reducing sign-up friction and potentially
increasing conversion. This process is best handled on your server. The client
sends the raw response (containing the `vp_token`) and the original nonce to a
new server endpoint.
For verification, your application must send the full `responseJsonString` to
your server for cryptographic validation before creating an account or logging
the user in.
The digital credential provides two critical levels of verification for your
server:
- **Authenticity of the data** : Verifying the issuer (`iss`) URL and the `SD-JWT` signature proves that a trusted authority issued this data.
- **Identity of the presenter** : Verifying the `cnf` field and the Key Binding (`kb`) signature confirms that the credential is being shared by the same device it was originally issued to, preventing it from being intercepted or used on another device.
The validation on the server must achieve the following:
- **Verify issuer** : Ensure the `iss` (issuer) field matches `https://verifiablecredentials-pa.googleapis.com`.
- **Verify signature**: Check the signature of the SD-JWT using the public keys (JWKs) available at https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.
> [!NOTE]
> **Note:** Use a standard library (such as [@sd-jwt/sd-jwt-vc](https://datatracker.ietf.org/doc/rfc9901/) for Node.js) to perform the verification steps as outlined in the [OpenID for Verifiable
> Presentations specification](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html).
For full security, make sure that you also validate the `nonce` to prevent
replay attacks.
By combining these steps, your server can validate both the authenticity of the
data and the identity of the presenter, ensuring the credential wasn't
intercepted or spoofed before provisioning the new account.
try {
// Send the raw credential response and the original nonce to your server.
// Your server must validate the response. createAccountWithVerifiedCredentials
// is a custom implementation per each RP for server side verification and account creation.
val serverResponse = createAccountWithVerifiedCredentials(responseJsonString, nonce)
// Server returns the new account info (e.g., email, name)
val claims = JSONObject(serverResponse.json)
val userInfo = VerifiedUserInfo(
email = claims.getString("email"),
displayName = claims.optString("name", claims.getString("email"))
)
// handle response - Up to the developer
} catch (e: Exception) {
// handle exceptions - Up to the developer
}
## Passkey creation
An optional but highly recommended next step after provisioning an account is to
immediately [create a passkey](https://developer.android.com/identity/passkeys/create-passkeys) for that account. This provides a secure,
passwordless method for the user to sign in. This flow is identical to a
standard passkey registration.
## WebView support
For the flow to work on a [`WebView`](https://developer.android.com/reference/android/webkit/WebView), developers should implement a
[JavaScript bridge](https://developer.android.com/identity/sign-in/credential-manager-webview) (JS Bridge) to facilitate the handoff. This bridge
allows the `WebView` object to signal the native app, which can then perform the
actual call to the Credential Manager API.
## See also
- [Overview of verified email retrieval](https://developer.android.com/identity/digital-credentials/email-verification)
- [Credential Manager](https://developer.android.com/identity/credential-manager)

View File

@@ -0,0 +1,145 @@
This document describes using Credential Manager to get a cryptographically
verified email address from a user's device. This process removes the need for
your app users to verify their email with one-time passwords (OTPs) or magic
links.
This document explains the following areas:
- Android compatibility
- User experience
- Accounts supported
- Implication of email deliverability
- Comparison with [Sign in with Google](https://developer.android.com/identity/sign-in/credential-manager-siwg)
This guide assumes you are familiar with the following concepts:
- [Credential Manager](https://developer.android.com/identity/credential-manager)
- [Digital Credentials](https://developer.android.com/identity/digital-credentials)
- [Verifiable Credentials](https://developer.android.com/identity/digital-credentials#verifiable-credentials)
## Android compatibility
This feature is supported on mobiles, tablets, and foldable devices running
Android 9 (API level 28) and higher. The minimum version of Google Play services
(GMS) required is 25.49.x.
## User experience
The following sections describe the user experience during the verification
flow, the need to include fallback verification methods, as well as the
recommended user experience for various use cases.
### The verification flow
The user experience for sharing a verified email is as follows:
1. The user either focuses on an input field or taps a button that calls the
Credential Manager API. Depending on the design of the screen, you can also
call the API on your app's screen load.
2. A bottom sheet appears, showing the information that will be shared with the
app. If no information is available on that device, the user sees a generic
error message.
3. After the user taps **Agree and Continue**, display a success or failure
message.
> [!NOTE]
> **Note:** If the verified email you receive does not match what you expect, inform the user about the mismatch and either ask them to try again with a different credential or provide an alternate verification method, such as through OTPs.
4. (Optional, recommended) If the user is signing up for your service, you
should prompt the user to [create](https://developer.android.com/identity/passkeys/create-passkeys) a [passkey](https://developer.android.com/identity/passkeys) to make it easier for
them to sign in subsequently.
> [!NOTE]
> **Note:** The email verification process doesn't automatically trigger passkey creation. However, it is highly recommended to include the steps for passkey creation. Passkeys help users by making it easier and more secure for them to sign in, and remove the need for the conventional username and password interaction.
### Include primary and fallback flows
To ensure a streamlined user experience, include the following options on
screens that require email verification:
- **Primary verification option**: An email field or button to trigger the Credential Manager API flow for quick verification.
- **Alternate verification options**: A link or button for users to "Verify another way" or with "Other options" for manual email entry in case of failures, such as no information available on the device, or a mismatch between the retrieved and expected email. This should allow users to try verification with a different credential or by providing a manual OTP.
### Use cases
The following sections describe the recommended use cases, as well as the
suggested user experience, for email verification.
#### Sign up
Users can immediately create an account with a verified email without a separate
verification step. Optionally, prompt the user to add a passkey. If they opt to
add a passkey, trigger the [passkey creation](https://developer.android.com/identity/passkeys/create-passkeys) flow.
![Using email verification during sign up, and then creating passkeys](https://developer.android.com/static/identity/digital-credentials/images/signup_ux.png) Email verification during sign up
#### Account recovery
To eliminate the frustration of users searching for recovery codes in their spam
folders, allow them to recover their account using the verified email securely
stored on their device. Additionally, suggest that they create a passkey for
future use.
![Using email verification during account recovery](https://developer.android.com/static/identity/digital-credentials/images/account_recovery_ux.png) Email verification during account recovery
#### Reauthentication for sensitive actions
Protect sensitive user actions, such as changing settings or updating profile
details, by requiring a quick reauthentication step.
![Using email verification during reauthentication](https://developer.android.com/static/identity/digital-credentials/images/reauthentication_ux.png) Email verification during reauthentication
## Accounts supported
Email verification through Credential Manager only supports verification of
consumer Google Accounts. [Workspace accounts](https://knowledge.workspace.google.com/admin/getting-started/set-up-google-workspace-for-your-organization) and [supervised
accounts](https://support.google.com/families/answer/9499054) are not supported.
A consumer Google Account can be created with an email address from any
provider, not necessarily @gmail.com. However, Google verifies these accounts
differently:
- For @gmail.com accounts: Google is the authoritative source, and the email is known to be verified.
- For non-@gmail.com accounts: Google is not the authoritative source for these email addresses in the long term. While Google verifies the email when the account is created, the ownership of that email address might change over time. Therefore, for non-@gmail.com addresses, you should consider an additional verification step, such as sending an OTP, to ensure that the user still has access to the email account.
For more information about what verification implies, see [Digital
Credentials](https://developer.android.com/identity/digital-credentials#verified).
> [!NOTE]
> **Note:** Apart from a user's email information, you can request other unverified fields, such as the user's given name, family name, name, and the profile picture of their Google Account. However, only the email is verified by Google.
> For phone number verification, see [Verify phone numbers with digital credentials](https://developer.android.com/identity/digital-credentials/phone-number-verification). For a Firebase solution, see [Firebase Phone Number Verification](https://firebase.google.com/docs/phone-number-verification). Note that you can't verify a phone number and email in the same invocation as these are separate capabilities.
## Validity and freshness
The system issues [verifiable credentials](https://developer.android.com/identity/digital-credentials#verified) (VCs) based on the user's current
email from the active Google Accounts on the device. These credentials are
issued to the device in advance, typically while the device is idle. While these
credentials might remain valid for multiple days, the system performs a check at
the moment of sharing the credentials to ensure that the account still exists,
is on the device, and that the email address is valid---effectively prioritizing
the account's immediate status over the credential's validity window.
To help ensure authenticity, a Key Binding (kb) signature is generated at the
time of sharing, incorporating the nonce.
If a device is offline or the account is removed, the process fails rather than
providing an expired VC or a VC for an inactive Google Account.
### Email deliverability
While the process confirms the account's legitimacy, it does not guarantee inbox
delivery (for instance, the email might be diverted to spam). An OTP remains the
definitive method for confirming email deliverability.
## Comparison with Sign in with Google
While both Digital Credentials and [Sign in with Google](https://developer.android.com/identity/sign-in/credential-manager-siwg) solutions provide a
verified email, the user flows and use cases are different:
- **Use cases**: The Credential Manager email verification flow is not exclusively used in sign up or sign in use cases, but rather can be used in any use case involving the retrieval of verified email. This could include account recovery as well.
- **Registration**: The Credential Manager flow does not require Google registration, unlike Sign in with Google.
- **Platform support**: The Credential Manager flow is an Android-only solution.
- **Scopes** : Unlike Sign in with Google, which can use OAuth 2.0 to request access to user data (such as Calendar or Drive through scopes), the Digital Credentials API is strictly for retrieving verified identity attributes. It cannot be used to request additional [authorization scopes](https://developers.google.com/identity/protocols/oauth2/scopes).
## Next steps
To implement this feature in your app, see the [Implementation guide](https://developer.android.com/identity/digital-credentials/email-verification-implementation).

View File

@@ -0,0 +1,96 @@
Digital credentials are cryptographically verifiable documents that can be used
to authenticate, authorize, or otherwise provide information about a user. These
are typically things such as mobile driver's licenses, digital passports,
boarding passes, etc. They reside in virtual containers called digital wallets,
and are part of a W3C standard that specifies how to access and retrieve them.
This standard is implemented for web use cases with the [W3C Credential
Management API](https://www.w3.org/TR/credential-management-1/) and on Android, with Credential Manager's
[DigitalCredential API](https://developer.android.com/reference/kotlin/androidx/credentials/DigitalCredential).
## Understand digital credentials
In the physical world, a person might keep their identity in their wallet, and
present it to a requesting party when asked:
![Image showing the flow of a normal wallet interaction](https://developer.android.com/static/identity/digital-credentials/images/normal_wallet_flowchart.svg) **Figure 1.** The process of fulfilling a physical-world credential request. The requestor asks the user for a specific credential. Then, the user selects and retrieves it from their physical wallet. Finally, the user provides the credential to the requestor.
In this case, a user generally has a single wallet, and retrieves the requested
credentials from the wallet to present to the requestor. Wallets are mostly
interchangeable, and can generally store the same things.
Digital credentials have the following differences from credentials in the
physical world:
1. Users are expected to have multiple wallets - also known as **holders** - which can contain various different credentials. Wallets determine which credentials may be stored inside of them.
2. The app or service asking for the credential to grant access or verify an identity is called the **verifier**.
3. The entity that creates the credential and asserts claims about the subject (such as, a university, a government, or a tech company) is referred to as the **issuer**.
4. The credential presentation happens in software, which means an API surface retrieves and presents the credentials - in Android, this is Credential Manager.
As such, Credential Manager takes on several roles that were formerly handled by
the user:
1. On Android, wallets must register their credentials metadata with Credential Manager to be listed in the Credential Manager UI.
2. Credential Manager matches credentials across wallets based on the request and presents a list for the user to select.
3. When the user selects a credential in the list, Credential Manager then invokes the wallet, which will handle the remainder of the transaction (showing UIs, etc.) and return the credential to the application.
This flow is shown here:
![Image showing the flow of a digital credential interaction](https://developer.android.com/static/identity/digital-credentials/images/digital_credentials_flowchart.svg) **Figure 2.** Interaction model for digital credential verification. Credential Manager uses pre-registered credentials metadata across user wallet(s) to match a verifier's request and prompts the user to select a credential. Credential Manager then directs the activity flow to the corresponding wallet which handles the remainder of the transaction and returns the credential to the verifier. Note: The verifier needs to handle and verify the credential response once it is returned.
## Verifiable credentials
Verifiable credentials are a subset of digital credentials governed by strict
standards (like the W3C Verifiable Credentials Data Model). These credentials
contain claims that are cryptographically secured, making them tamper-evident
and proving exactly who issued them.
Not all digital credentials are verifiable credentials, but all verifiable
credentials are digital credentials.
## What it means for a claim to be verified
When a credential arrives through the Android Credential Manager API and a claim
within it is marked as "verified," it implies that the issuer is asserting that
they performed a check on that specific piece of data. However, it does not mean
the data is an absolute, universal truth. "Verified" is an assertion of process,
not an automatic guarantee of trust.
The core philosophy of this ecosystem is that trust is always resolved at the
verifier. When the verifier (your app) receives the cryptographically secure
data, and sees that the issuer marked it as "verified," it must determine
whether it trusts the issuer to have verified the claim to its standards.
### User experience
As shown in the Android flow, the user only needs to interact once with the
Credential Manager UI to select the appropriate credential. Here is an example
of how the selector looks:
![Image showing the digital credentials UI in Credential Manager](https://developer.android.com/static/identity/digital-credentials/images/digital_credentials_ui.png) **Figure 3.** The digital credentials UI.
### Standards
Digital credentials requests are created using the [OpenID4VP
standard](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-introduction). You can see example requests at the [Digital
Credentials Demo site](https://digital-credentials.dev/).
Digital credential responses are typically returned in a standardized credential
format. These are maintained by different standards bodies, and include [W3C
Verifiable Credentials](https://www.w3.org/TR/vc-data-model-2.0/), [sd-jwt](https://datatracker.ietf.org/doc/draft-ietf-oauth-selective-disclosure-jwt/), and
[mdoc](https://www.iso.org/standard/69084.html).
Custom protocols are also feasible, though we recommend using one of the
standard protocols in your application.
### Try it out
You can test out the digital credentials flow across platforms with an Android
wallet and web-based verifier:
1. Install the [CMWallet public sample](https://github.com/digitalcredentialsdev/CMWallet) on your Android phone. You can do this by pulling from the repository and installing directly from Android Studio or navigating to <https://github.com/digitalcredentialsdev/CMWallet/actions> and selecting the latest build to access the latest `app-debug.apk` file.
2. Open the CMWallet to register the metadata with Credential Manager. Make sure Bluetooth is enabled to allow your devices to connect to each other.
3. Navigate to <https://digital-credentials.dev/> and select `Request Credentials (OpenID4VP)`.
4. Accept the warning prompts and scan the QR Code with your phone, then select "Use passkey" and tap through the confirmation to show the available credentials.
5. Select the credential from CMWallet to return to the browser. The browser should show the returned credential.
### See also
- To learn more about using Credential Manager to request digital credentials in your app, read the [Credential Manager - Verifier API](https://developer.android.com/identity/digital-credentials/credential-verifier) page.
- To learn more about building a digital wallet using Credential Manager, read the [Credential Manager - Holder API](https://developer.android.com/identity/digital-credentials/credential-holder) page.