flutter_digital_credentials 0.1.0-dev.2 copy "flutter_digital_credentials: ^0.1.0-dev.2" to clipboard
flutter_digital_credentials: ^0.1.0-dev.2 copied to clipboard

Android Credential Manager Digital Credentials verifier bridge for Flutter.

flutter_digital_credentials #

Experimental Android-only Flutter bridge for Credential Manager Digital Credentials.

The plugin opens Android's system credential selector with an OpenID4VP request issued by your verifier backend, then returns the original presentation JSON. It does not cryptographically verify claims, create users, or issue a session. Send every presentation to a trusted backend for verification.

Status #

The AndroidX Digital Credentials API used by this package is experimental. The current implementation compiles against androidx.credentials:1.7.0-alpha03. This flow was verified end-to-end on a Motorola edge 60 fusion running Android 16 with the Google provider and the included local verifier. It remains an experimental API integration, not production identity infrastructure.

This is the Android system consent sheet shown by the verified-email example on the tested Motorola device. The account photo, name, and email are redacted in black; all remaining UI is captured from the real provider flow.

[Android system sheet for sharing a verified email credential, with account details redacted]

Requirements #

  • Android 6 / API 23 or later at runtime.
  • A compatible Google Play services and Digital Credentials provider setup.
  • A verifier backend that generates the OpenID4VP request, binds a single-use nonce, and verifies the returned presentation.

Usage #

final credentials = FlutterDigitalCredentials();

// Fetch this JSON from your backend. Do not generate a production nonce in the
// Flutter client.
final requestJson = await verifierApi.createVerifiedEmailRequest();

try {
  final presentation = await credentials.request(requestJson: requestJson);

  // Send the raw presentation to the same trusted backend. It must verify the
  // issuer, SD-JWT signature, key binding, audience/origin, nonce, expiry and
  // application claim policy before creating a session.
  await verifierApi.completeVerifiedEmail(presentation.credentialJson);
} on DigitalCredentialNotFoundException {
  // Offer OTP or magic-link fallback.
} on DigitalCredentialCancelledException {
  // The user dismissed the system flow.
}

DigitalCredentialPresentation.credentialJson is unverified transport data.

Protocol models #

Use OpenId4VpRequest, DcqlCredentialQuery, and DcqlClaimQuery when your backend needs Dart to serialize an already-authorized request. The backend must still generate the nonce and own verifier policy.

final request = OpenId4VpRequest(
  nonce: serverNonce,
  credentials: [
    DcqlCredentialQuery(
      id: 'credential',
      format: 'dc+sd-jwt',
      meta: const {'vct_values': ['ExampleCredential']},
    ),
  ],
);

final presentation = await credentials.requestOpenId4Vp(request: request);
final response = presentation.parseOpenId4VpResponse();
final token = response.presentationsFor('credential').single;

The parser supports standard, legacy, and digital-wrapped response envelopes. It throws DigitalCredentialProtocolException for a protocol error or malformed envelope. Parsed tokens remain opaque and unverified.

Age-gate request (18+) #

OpenId4VpRequest.mdlAgeOver18 requests the selective-disclosure age_over_18 predicate from an ISO mDL credential. It deliberately does not request a birth date, name, address, or licence number.

final request = OpenId4VpRequest.mdlAgeOver18(nonce: serverNonce);
final presentation = await credentials.requestOpenId4Vp(request: request);

// The backend must validate this as a trusted issuer's mDL response before
// allowing access to an age-gated feature.
await verifierApi.completeAgeGate(presentation.credentialJson);

The helper does not provide a wallet, an issuer, regional mDL availability, or an age-verification decision. The Android API supports requests for age attributes; each product must obtain an appropriate issuer and implement its own server-side trust and access policy.

API reference #

Member Description
FlutterDigitalCredentials.request Opens Android Credential Manager with server-supplied request JSON and returns the raw presentation.
FlutterDigitalCredentials.requestOpenId4Vp Serializes a typed OpenID4VP/DCQL request, then opens Android Credential Manager.
DigitalCredentialPresentation.credentialJson Opaque, unverified provider response. Send it unchanged to the verifier.

Runnable verified-email example #

The included example is a full local verifier flow: it obtains a one-time OpenID4VP request from tool/verified_email_verifier, opens Credential Manager, and posts the raw presentation back for server-side verification. Never use decoded client-side claims as proof of identity or account ownership.

To run it on a USB-connected Android device:

cd tool/verified_email_verifier
npm install
npm start

# In another terminal, at the repository root:
adb reverse tcp:8787 tcp:8787
cd example
flutter run

The debug app connects to http://127.0.0.1:8787 through adb reverse. This cleartext route exists only in the debug manifest. See the verifier guide for its local-only security boundaries.

Backend contract #

The companion architecture guide describes a reference TypeScript verifier with a short-lived, single-use verification session:

  1. POST /verification-sessions returns sessionId and OpenID4VP request JSON.
  2. Flutter calls request() and posts the raw response to POST /verification-sessions/{id}/complete.
  3. The backend validates the presentation before returning an account result.

The package is backend-neutral. The reference architecture chooses Node because Android's verified-email guide explicitly recommends @sd-jwt/sd-jwt-vc for server-side verification. Android implementation guide

Errors #

The public API exposes typed exceptions:

  • DigitalCredentialsUnsupportedException
  • DigitalCredentialNotFoundException
  • DigitalCredentialCancelledException
  • DigitalCredentialInvalidRequestException
  • DigitalCredentialInterruptedException
  • DigitalCredentialProviderException
  • DigitalCredentialProtocolException
  • DigitalCredentialUnknownException

Messages are deliberately sanitized and do not include credential material.

Example #

The Flutter example calls the reference verifier automatically. Its only action is Request verified email; it never renders raw credential JSON.

Scope #

This version supports verifier retrieval, generic DCQL/OpenID4VP protocol models, and construction of a selective-disclosure ISO mDL 18+ request. Credential holder/wallet registration, issuance, phone-number exchange, government IDs, age-policy verification, and Firebase Auth are not included.

Verified email is useful for signup or low-risk re-verification. It is not a universal account-recovery factor: an email claim does not always establish inbox freshness or satisfy a product's recovery policy.