Skip to content

Define "present credential requests" algorithm.#419

Open
marcoscaceres wants to merge 12 commits intomainfrom
present
Open

Define "present credential requests" algorithm.#419
marcoscaceres wants to merge 12 commits intomainfrom
present

Conversation

@marcoscaceres
Copy link
Collaborator

@marcoscaceres marcoscaceres commented Dec 16, 2025

Adds "present credential requests" algorithm that describes how to present a credential request, including constructing request data, invoking the credential chooser, handling user cancellation, abort signals, platform errors, and processing the selected digital credential.

Closes #???

The following tasks have been completed:

  • Modified Web platform tests (link)

Implementation commitment:

  • WebKit
  • Chromium (link to issue)
  • Gecko (link to issue)

Documentation and checks

  • Affects privacy
  • Affects security
  • Pinged MDN
  • Updated Explainer
  • Updated digitalcredentials.dev

Preview | Diff

@marcoscaceres marcoscaceres marked this pull request as ready for review January 13, 2026 12:51
@marcoscaceres marcoscaceres requested a review from a team as a code owner January 13, 2026 12:51
@marcoscaceres marcoscaceres changed the title Define "Present credential requests" algorithm. Define "present credential requests" algorithm. Jan 13, 2026
@marcoscaceres marcoscaceres added the agenda+ Add to the weekly agenda label Jan 20, 2026
webkit-commit-queue pushed a commit to marcoscaceres/WebKit that referenced this pull request Jan 20, 2026
…if AbortSignal races with picker result

rdar://163295172
https://bugs.webkit.org/show_bug.cgi?id=305363

Reviewed by Pascoe.

Fix race and crash in CredentialRequestCoordinator by settling promises only after picker teardown and safely handling abort reasons.
Ensure credential requests always settle after the picker UI has fully torn down.

This change:

- Defers promise settlement until the picker dismiss callback fires
- Better handles aborts during presentation and teardown
- Avoids capturing unprotected JSValues across async boundaries
- Keeps coordinator state transitions more consistent (with better checks)

It also more closely follows the spec:
w3c-fedid/digital-credentials#420
w3c-fedid/digital-credentials#419

* LayoutTests/imported/w3c/web-platform-tests/digital-credentials/get.tentative.https-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/digital-credentials/non-fully-active.https-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/digital-credentials/non-fully-active.https.html:
* Source/WebCore/Modules/identity/CredentialRequestCoordinator.cpp:
(WebCore::CredentialRequestCoordinator::PickerStateGuard::PickerStateGuard):
(WebCore::CredentialRequestCoordinator::PickerStateGuard::~PickerStateGuard):
(WebCore::CredentialRequestCoordinator::setState):
(WebCore::CredentialRequestCoordinator::prepareCredentialRequest):
(WebCore::CredentialRequestCoordinator::handleDigitalCredentialsPickerResult):
(WebCore:: const):
(WebCore::CredentialRequestCoordinator::dismissPickerAndSettle):
(WebCore::CredentialRequestCoordinator::abortPicker):
(WebCore::CredentialRequestCoordinator::contextDestroyed):
(WebCore::CredentialRequestCoordinator::~CredentialRequestCoordinator):
(): Deleted.
(WebCore::CredentialRequestCoordinator::presentPicker): Deleted.
(WebCore::CredentialRequestCoordinator::finalizeDigitalCredential): Deleted.
* Source/WebCore/Modules/identity/CredentialRequestCoordinator.h:
* Source/WebCore/Modules/identity/DigitalCredential.cpp:
(WebCore::DigitalCredential::discoverFromExternalSource):
* Source/WebCore/SaferCPPExpectations/UncountedCallArgsCheckerExpectations:

Canonical link: https://commits.webkit.org/305868@main
index.html Outdated
Comment on lines 814 to 818
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed then?

Suggested change

|document|'s [=relevant settings object=]'s [=environment settings
object/origin=], and |document|'s [=Document/origin=].
</li>
<li>Present a [=credential chooser=] with |requestData| and wait for the
Copy link
Contributor

@mohamedamir mohamedamir Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[=credential chooser=]

The credential choose linked here is the CredMan chooser which is different from the chooser intended by this algorithm I assume.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, yeah... this is where the "digital wallet" concept would have helped.... I guess even then, it would still be "show a UI to select a digital wallet with request data" and the digital wallet itself is a [=credential chooser=].

Could you help me with the wording here, @mohamedamir ... or we can come up with something together when we next chat?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is one proposal:
What do you think that we define in our spec a digital credentials picker
And define as the UI displayed by the underlying platform together with digital wallets to let the user select a digital credentials in response to a digital credential presentation request.

And we can then refer to that one in our spec.
WDYT?

Happy to discuss/brainstorm in our weekly as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'm fine with that.

</li>
<li>If the user cancels the operation or no credential was selected:
<ol>
<li>Let |error| be a newly created {{"AbortError"}} {{DOMException}}.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why AbortError when no credential was selected or when the user cancels the operation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In both cases, "the operation was aborted [by the user]." I don't think we should distinguish them. User cancelling the operation is generally an AbortError.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree :-)
in webauthn get-algorithm-step 16

If the user exercises a user agent user-interface option to cancel the process, 
For each authenticator in issuedRequests invoke the authenticatorCancel operation on authenticator 
and remove authenticator from issuedRequests. 
Return a DOMException whose name is "NotAllowedError".

and even more explicit in the level 3

AbortError The ceremony was cancelled by an AbortController. 
See § 5.6 Abort Operations with AbortSignal and § 1.3.4 Aborting Authentication Operations.
.
.
.
NotAllowedError A catch-all error covering a wide range of possible reasons, 
including common ones like the user canceling out of the ceremony. 
Some of these causes are documented throughout this spec, while others are client-specific.

And Firefox even fixed a bug related to that for webauthn.

CredMan however, returns null when cancelling the credential chooser IIUC.

But I do think we shouldn't mix both, user cancelling and developer cancelling the request

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This spec is not Web Auth and we can do better. Using a catchall is not a good thing. It means developers will rely on error messages. People criticize Cred Man and Web Auth AFAIK for not using a range of exceptions.

We can elevate this to the TAG if need be, but seems like a not great use of time.

I’m strongly of the opinion that this should be an AbortError. We have a great range of exception types for a reason - we should use them.

</li>
</ol>
</li>
<li>If the platform returns a platform-specific error:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the first mention of the platform, right?
So far it sounded like the browser is doing all of it.
Am I overlooking something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. However, presenting the UI and so on was presumedly done by the platform too... should clarify that.

For what it's worth, in WebKit, we have the following errors coming back potentially from the OS (from the "Identity Document Framework", which is provided by the OS/platform):

    switch (error.code) {
    case WKIdentityDocumentPresentmentErrorNotEntitled:
        exceptionData = { ExceptionCode::NotAllowedError, "Not allowed because not entitled."_s };
        break;
    case WKIdentityDocumentPresentmentErrorInvalidRequest:
        exceptionData = { ExceptionCode::TypeError, "Invalid request."_s };
        break;
    case WKIdentityDocumentPresentmentErrorRequestInProgress:
        exceptionData = { ExceptionCode::InvalidStateError, "Request already in progress."_s };
        break;
    case WKIdentityDocumentPresentmentErrorCancelled:
        exceptionData = { ExceptionCode::AbortError, "Request was cancelled."_s };
        break;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from the AbortError being discussed in another comment thread, I think it's always the platform that does the credential selection.
Even in WebKit, IIUC, it is the platform that does it.
Maybe we should make it clearer in the text of the algorithm in earlier steps?
I don't know, it might be surprising to some when first hear about the platform here and not sure about its role.

WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that makes sense. Open to suggestions or can try to clarify things.

Copy link
Collaborator Author

@marcoscaceres marcoscaceres Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed with @mohamedamir ... will switch WKIdentityDocumentPresentmentErrorNotEntitled to NotAllowedError.

For default case where the error is unknown, we should default to "OperationError".

I'll rewrite these in a general form and @mohamedamir will check if they align with Android's platform errors.

</li>
<li>If a [=digital credential=] was selected by the user:
<ol>
<li>Let |responseData| be a [=string=] [=digital
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this guaranteed to be a string?
should we document this somewhere that this is the expected format?

Copy link
Collaborator Author

@marcoscaceres marcoscaceres Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it has to be a string, as we have to:

  1. Check if JSON parsable/serializable (otherwise .toJSON() would break).
  2. Create the JS Object in the right JS realm (which obviously no native app can do for us).
  3. Check it is a JS Object (because the WebIDL requires it for .data).

Copy link
Contributor

@mohamedamir mohamedamir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the direction in general, I have added a couple of comments

Thank you, @marcoscaceres

Comment on lines +683 to +692
<li>Present a [=credential chooser=] with |requestData| and wait for the
user to either:
<ul>
<li>Select a [=digital credential=] and [=holder=] that can fulfill
the request, or
</li>
<li>Cancel the operation.
</li>
</ul>
</li>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The text below this makes it clear that we might exit the credential chooser due to the abort controller or an error. Those cases should be mentioned here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, @jschanck... I'll describe them.

@hlflanagan
Copy link

Discussed in 21 January 2026 Series B call

|document|'s [=relevant settings object=]'s [=environment settings
object/origin=], and |document|'s [=Document/origin=].
</li>
<li>Present a [=credential chooser=] with |requestData| and wait for the
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to clarify what happens when there's multiple matching requests or equivalent requests that are valid to present.

@hlflanagan
Copy link

Discussed on 2026-02-09 call. Editors will bring this back to the group after more research for best practices and to suggest direction.

@mohamedamir mohamedamir removed the agenda+ Add to the weekly agenda label Feb 12, 2026
<ul>
<li>Select a [=digital credential=] and [=holder=] that can fulfill
the request, or
</li>
Copy link
Collaborator Author

@marcoscaceres marcoscaceres Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed with @mohamedamir, we are going to slot in an issue here for "Presenting with CTAP #456" so it's clear when CTAP comes into play. And we can craft text around the requirements for cross device / cross platform support here of CTAP and potentially the protocols.

note that this is independent of "Mandate one presentation protocol MUST be supported" #454.

{{AbortSignal}} |signal|:
</p>
<ol class="algorithm">
<li>Let |requestData| be [=struct=] consisting of |validatedRequests|,
Copy link
Collaborator Author

@marcoscaceres marcoscaceres Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed with @mohamedamir and @simoneonofri... I'm going to split this out into two variables, so it's more clear.

In WebKit, this looks like:

struct DigitalCredentialsRequestData {
    Vector<ValidatedMobileDocumentRequest> requests;
    SecurityOriginData topOrigin;
    SecurityOriginData documentOrigin;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants