Websites that ask users to present a wallet credential through QR codes and custom URL schemes expose them to relay phishing, broken wallet selection and inconsistent behaviour across platforms. The W3C Digital Credentials API gives the browser and operating system a defined role: a site calls navigator.credentials.get() with a digital member, the platform finds a matching credential in an installed wallet, and the encrypted response comes back to the page. Chrome enabled it by default in version 141, Safari supports it for ISO mdoc requests, and the EUDI Wallet framework treats it as a key remote presentation channel. This guide is for web and identity engineers adding the API to a relying party site: the API surface, supported protocols, browser behaviour, the security model and what it does not cover, implementation steps, testing, monitoring and common mistakes. For what a relying party must do beyond the browser call, see the Relying Party Integration with Digital Identity Wallets Guide.
Key takeaways
- It is a transport, not a protocol: the API carries an OpenID4VP or ISO mdoc request and response; your server still builds the request, decrypts the response and verifies every signature.
- The status is still a Working Draft: the W3C Digital Credentials specification was a Working Draft dated 20 October 2026 when checked, so code to feature detection and expect changes.
- Origin binding is the main security gain: with OpenID4VP over the API, the presentation audience is the calling origin prefixed by
origin:, which defeats a phishing site relaying your request. - Platforms differ: Chrome and Edge are format and protocol agnostic, while Safari supports only the ISO/IEC 18013-7 Annex C mdoc profile, and Apple Wallet requires a reader authentication certificate from Apple Business Connect.
- Always keep a fallback: unsupported browsers, missing wallets and refusals all look the same to the site (
NotAllowedErrorfor refusals), so design a second path at equivalent assurance.
What the API is and where it sits
The Digital Credentials API extends Credential Management Level 1, the same API family used by WebAuthn and passkeys, so that a site can request (present) or offer (issue) a digital credential such as a driving licence, identity card or other attestation. It is designed to be agnostic to credential formats. The specification moved from incubation in the Web Platform Incubator Community Group to a W3C Working Draft; the EUDI Architecture and Reference Framework discussion paper on the topic (Topic F, version 1.3, November 2025) still describes the earlier community group status, which shows how quickly it has moved.
| Layer | Responsibility | Defined by |
|---|---|---|
| Website (relying party) | Calls the API after a user gesture, passes server-built request data, sends the opaque response to the back end | W3C Digital Credentials |
| Browser | Checks secure context, user activation, permissions policy and origin; mediates the request; relays the response without reading it | W3C Digital Credentials |
| Operating system | Matches the request to credentials in installed wallets and shows a selector; on Android wallets provide a sandboxed matcher, on iOS wallets register supported document types | Platform specific, out of scope of the specification |
| Cross-device link | Connects a desktop browser to a phone; current implementations use the CTAP 2.2 hybrid flow with a QR code and a Bluetooth Low Energy proximity check | FIDO CTAP |
| Exchange protocol | Request and response format, encryption and holder binding | OpenID4VP 1.0 or ISO/IEC 18013-7 Annex C |
| Credential format | SD-JWT VC or ISO mdoc and their signatures | IETF and ISO |
The specification itself treats OS compromise, malicious credential managers and weaknesses in protocols or formats as out of scope. Those remain the relying party's and wallet's problem. See the OpenID for Verifiable Presentations (OpenID4VP) Guide and the Mobile Driving Licence (mDL) Guide.
API surface and protocol identifiers
- Presentation:
navigator.credentials.get({ mediation: "required", digital: { requests: [ { protocol, data } ] } }). Each entry inrequestsnames an exchange protocol and carries protocol-specificdata; offering several lets the platform pick one the wallet supports. - Issuance:
navigator.credentials.create()with the same request structure, for credential offers. - Result: a
DigitalCredentialobject exposingprotocolanddata; the data is encrypted for your server and opaque to the browser. - Feature detection: check
typeof DigitalCredential !== "undefined", thenDigitalCredential.userAgentAllowsProtocol("openid4vp-v1-signed"). The specification requires that this answer does not vary by hardware, installed software or settings, to limit fingerprinting. - Cancellation: the call accepts an
AbortSignalthrough the standard Credential Management options, so a page can time out a stalled request.
| Protocol identifier | Purpose | Notes |
|---|---|---|
openid4vp-v1-unsigned | OpenID4VP presentation, request not signed | Wallet identifies the verifier by origin only; suitable where the wallet does not need verifier identity beyond the origin |
openid4vp-v1-signed | OpenID4VP presentation with a signed request object | Lets the wallet authenticate the relying party, for example with an EUDI access certificate |
openid4vp-v1-multisigned | OpenID4VP presentation signed by several parties | For ecosystems that need more than one trust framework at once |
org-iso-mdoc | ISO/IEC 18013-7 Annex C mdoc request | The only profile Safari supports; uses ISO ReaderAuth for request authentication |
openid4vci-v1 | OpenID4VCI credential issuance | Recommended, not required, for user agents; experimental in Chrome and Edge according to the ARF |
The four presentation identifiers are listed as required for user agents in the Working Draft. HAIP 1.0, the OpenID high assurance profile, requires verifiers using the API flow to use the dc_api.jwt response mode (encrypted responses) and to support at least one of unsigned, signed or multi-signed requests. See the OpenID for Verifiable Credential Issuance Guide for the issuance side.
Browser and platform behaviour
| Platform | Behaviour (sources checked 27 October 2026) | What it means for you |
|---|---|---|
| Chrome 141 and later, Android | Enabled by default since Chrome 141 (October 2025); same-device presentation with a built-in credential picker; requires Google Play services 24.0 or higher | Most Android users with a supporting wallet app can present without leaving the page |
| Chrome desktop | Cross-device: user scans a QR code with the phone; CTAP and BLE confirm the phone is nearby | Proximity check makes remote relay of the QR code much harder than with plain OpenID4VP QR codes |
| Safari 26 and iOS 26 | Supports mdoc requests through the API; Chrome and other browsers on iOS 26 also gain support | Only org-iso-mdoc is supported; for Apple Wallet, sign requests with a certificate from Apple Business Connect |
| Edge | Presentation available (behind a feature flag according to the ARF in October 2025) | Test explicitly; do not assume Chrome parity |
| Older or other browsers | DigitalCredential undefined | Fall back to OpenID4VP redirect flow or another verification method |
The earlier origin trial used navigator.identity.get() with a providers member and request fields; Chrome's shipped API uses navigator.credentials.get() with requests and data. Remove any trial-era code paths.
Security model: what the API gives you and what it does not
Protections the platform adds
- Secure context and user activation: the API works only over HTTPS and requires transient activation, so a page cannot fire requests silently; Apple's documentation states the call throws without a user interaction.
- Mandatory mediation: user mediation cannot be overridden, so the platform always shows its selector.
- Cross-origin control: embedded frames need the Permissions Policy features
digital-credentials-getordigital-credentials-create, which default to'self'. Third-party iframes cannot request credentials unless you delegate explicitly. - Origin binding: in OpenID4VP over the API, the presentation audience is always the origin prefixed by
origin:(for exampleorigin:https://verifier.example.com/), and wallets must not accept the reservedoriginclient identifier prefix inside requests. In the mdoc profile the Annex CSessionTranscriptplays this role. - Session binding: the API keeps the request and response in one browser context, which is why OpenID4VP drops its
staterequirement for presentations without holder binding when the API is used. - Uniform errors: user refusal and browser refusal both return
NotAllowedError, so sites cannot infer configuration.
Gaps you must close
| Threat | Why the API does not solve it | Relying party control |
|---|---|---|
| Forged or replayed presentation | Browser relays opaque data and does not verify credentials | Server-side nonce per request, signature and status checks, holder binding |
| Cross-site scripting on your page | Injected script on your origin can call the API as you and read the response | Strict Content Security Policy, no third-party script on the verification page, encrypted responses decrypted only on the server |
| Untrusted issuer | The platform does not decide which issuers or relying parties are authorised | Issuer trust list (EUDI Trusted Lists or IACA roots for mdoc) and rejection of others |
| Social engineering | A real user can still be talked into presenting a credential to an attacker's own legitimate-looking site | Clear purpose text, minimal requests, transaction data for payments; see the Digital Identity Wallet Fraud Risks Guide |
| Platform profiling | The ARF notes Member State concern that platforms could infer sensitive facts from requested document types and attribute names | Request only what you need; avoid revealing sensitive context in document type choice where alternatives exist |
| Decryption key exposure | Response encryption keys live on your server | Ephemeral key per request, kept in memory or an HSM; Apple advises keeping the key safe and rotating it frequently |
Where identity fits
Signed requests rely on relying party keys: an EUDI access certificate for OpenID4VP or an Apple Business Connect reader authentication certificate for Apple Wallet mdoc requests. These are non-human credentials that let anyone request identity data in your name. Hold signing keys in an HSM or managed key service, keep the signing service separate from the web tier, and alert on unusual signing volume. See the Machine Identity, PKI and Certificate Lifecycle Guide.
Choosing between the API and other invocation methods
- Use the Digital Credentials API for browser journeys where it is supported: it removes custom URL schemes, gives a consistent wallet picker and adds origin and proximity binding.
- Keep the OpenID4VP redirect flow (signed request by
request_uri,direct_post.jwt, same-session check) for browsers without support and for wallets that do not integrate with the platform API. HAIP requires rejecting responses where the redirect does not come back in the same user session. - Use proximity protocols (ISO/IEC 18013-5) for in-person checks; the browser API is for remote web flows.
- Native apps use the platform credential APIs rather than the web API; the protocol and verification logic on your server stay the same.
- Small organisations can use a verifier library or service that already supports both
openid4vp-v1-*andorg-iso-mdoc; large organisations should own the request builder, key management and verification so they can meet registration and audit duties.
For the wider risks of QR-based flows, see the Cross-Device Flow Security Guide.
Implementation steps
- Decide the credentials and protocols: which credentials (PID, mDL, other attestations), which formats, and therefore which protocol identifiers you will offer. To reach Safari users you need
org-iso-mdoc; for EUDI wallets you need OpenID4VP. - Build requests on the server: generate a fresh nonce and ephemeral encryption key pair per request; build a DCQL query (OpenID4VP) or
DeviceRequestwithItemsRequest,docTypeandnameSpaces, marking the intent to retain each element honestly (mdoc). Sign where the ecosystem expects it. - Call the API from a user gesture: feature-detect, then call
navigator.credentials.get()inside a click handler withmediation: "required"and one or morerequestsentries; set a timeout withAbortSignal. - Return the opaque response to the server: POST
protocolanddatato your back end bound to the server-side session that created the request; never verify in the browser. - Decrypt and verify: for OpenID4VP, decrypt the JWE, check the KB-JWT audience is
origin:plus your origin, and validate nonce, issuer signature, status and holder binding. For mdoc, decrypt with HPKE (RFC 9180) as Annex C specifies, then run ISO/IEC 18013-5 issuer data authentication against your IACA trust store and mdoc authentication with the device key. - Handle errors and fallback: on
NotAllowedError, timeouts or no support, offer the alternative journey without revealing why. - Harden the page: strict Content Security Policy, no third-party tags on the verification route, and a Permissions Policy that does not delegate
digital-credentials-getto iframes you do not control. - Test and roll out: test on Chrome Android, Chrome desktop with a phone, Safari on iOS and macOS, and an unsupported browser; pilot behind a feature flag, compare completion and error rates, and roll back by disabling the flag so users take the existing flow.
Detection and measurement
Log on the server per request: protocol identifier offered and used, platform family (from your own client hints, not fingerprinting), request ID, outcome of decryption, origin and nonce checks, issuer and status validation, and the final decision. Do not log credential payloads.
- API availability rate: share of sessions where
DigitalCredentialexists and your protocols are allowed. - Completion rate by protocol and platform: highlights wallet or platform integration problems.
- Verification failure rate by reason: origin or audience mismatch, nonce reuse, issuer not trusted, revoked, device authentication failed. Origin mismatches deserve investigation.
- Fallback share: how many users take the alternative path, and its fraud rate compared with the API path.
- Key hygiene: age of signing certificates and confirmation that response encryption keys are unique per request.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev. 5 | IA-8(2) Acceptance of External Authenticators, IA-8(4) Use of Defined Profiles | Accepting wallet credentials under defined profiles such as HAIP |
| NIST SP 800-53 Rev. 5 | SC-23 Session Authenticity, SC-8 Transmission Confidentiality and Integrity | Origin and session binding, encrypted responses |
| NIST SP 800-53 Rev. 5 | SC-12 Cryptographic Key Establishment and Management, SC-17 Public Key Infrastructure Certificates | Ephemeral encryption keys and signing certificates |
| NIST CSF 2.0 | PR.AA-03, PR.DS-02 | Authentication of users and data in transit |
| OWASP ASVS 5.0 | V3.4.3 Content Security Policy | Prevents injected script calling the API as your origin |
Common failure patterns
- Verifying in the browser: trusting client-side JavaScript to check signatures or attribute values lets an attacker post anything to the back end.
- Reusing nonces or encryption keys: a static key pair or nonce across requests enables replay and widens the impact of a key leak.
- Skipping the origin check: not validating the
origin:audience orSessionTranscriptthrows away the API's main phishing protection. - Offering only one protocol: OpenID4VP-only sites miss Safari users; mdoc-only sites miss SD-JWT VC wallets.
- Third-party script on the verification page: analytics or tag manager code running on the same origin can call the API or read responses.
- Coding to the origin trial: leftover
navigator.identity.get()paths silently fail on current browsers.
Practitioner checklist
- Feature-detect
DigitalCredentialand useuserAgentAllowsProtocol()before offering the option. - Offer both
openid4vp-v1-signed(or unsigned) andorg-iso-mdocwhere your credentials allow. - Build requests server-side with a fresh nonce and ephemeral encryption key per request.
- Call the API only from a user gesture with
mediation: "required"and a timeout. - Decrypt and verify on the server, including the
origin:audience or Annex CSessionTranscript, issuer trust, status and device binding. - Keep signing keys for requests in an HSM, separate from the web tier.
- Apply a strict Content Security Policy and keep third-party script off verification pages; do not delegate the permissions policy features to untrusted iframes.
- Maintain an equivalent-assurance fallback and track its use and fraud rate.
- Re-check the specification and browser release notes each quarter while the draft evolves.
Standards and references
- W3C: Digital Credentials
- OpenID Foundation: OpenID for Verifiable Presentations 1.0
- OpenID Foundation: OpenID4VC High Assurance Interoperability Profile 1.0
- Google Chrome for Developers: Digital Credentials API shipped
- Apple Developer: Requesting a mobile document on the web
- European Commission: EUDI Wallet ARF, Topic F Digital Credentials API
- NIST: SP 800-53 Rev. 5
Related NHI Mgmt Group resources: Digital Identity Wallets Guide · Relying Party Integration with Digital Identity Wallets Guide · OpenID for Verifiable Presentations (OpenID4VP) Guide · Passwordless and Passkeys Guide