Skip to content

Conversation

@cameronwhitworthforgerock
Copy link

@cameronwhitworthforgerock cameronwhitworthforgerock commented Jan 6, 2026

JIRA Ticket

Ticket

Description

Change enables support for autofill conditional UI in Javascript SDK.

Relates to https://pingidentity.atlassian.net/browse/OPENAM-23288

@changeset-bot
Copy link

changeset-bot bot commented Jan 6, 2026

⚠️ No Changeset found

Latest commit: a31207f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

@ryanbas21 ryanbas21 left a comment

Choose a reason for hiding this comment

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

We need to ensure that because this is a newer browser feature we are backwards compatible with browsers that may not support this feature.

So graceful degradation of conditional ui should occur where the browser doesn't break the user and we can at least continue the flow as needed.

Any details regarding this would be helpful because i'm fairly new to this feature

This is specific to authentication right? Is this testable with virtual authenticators? If we can pre-populate a user with a passkey in AM, i think we should be able to create an e2e for this?

If not, can we add an e2e test that at least can be run manually (doesn't need actual assertions, but it would be helpful for us to document how to do this work in code)

Also, please add a changeset to this PR, it should be a new feature, with a description of the feature being added so we can release correctly.

credentialRequestOptions.mediation = 'conditional' as CredentialMediationRequirement;
} else {
// eslint-disable-next-line no-console
console.warn('Conditional UI was requested, but is not supported by this browser.');
Copy link
Contributor

Choose a reason for hiding this comment

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

we need to use logger for this, in case a user does not want console log's to happen. the logger module will call console logs if enabled.

Choose a reason for hiding this comment

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

Done, shout if this is correct/incorrect please.

Choose a reason for hiding this comment

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

Comment resolved.

Removed console.warn

Copy link
Contributor

Choose a reason for hiding this comment

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

FRLogger itself handles the console log, so you can remove the duplicate console.warn statement.

}

interface WebAuthnAuthenticationMetadata {
_action?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we have a better type than a string for this? seems like we can have 'webauthn_authentication', I assume the alternative is 'webauthn_registration'?

Choose a reason for hiding this comment

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

Done, see below

Copy link
Contributor

Choose a reason for hiding this comment

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

You mention that this change is done, but I still see string as the value for _action. Am I missing something?

Choose a reason for hiding this comment

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

If you check the file changes rather than these comments, you should see the change. For reason beyond me, github has decided to move this comment to Outdated so it can only be observed here.
Hope that makes sense.

userVerification: UserVerificationType;
conditionalWebAuthn?: boolean;
extensions?: Record<string, unknown>;
_type?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

What are the possible _type's that we can get? Ideally we'd like to be more strict than string where possible. In the test data I see WebAuthn but what other options are there?

What role does _type play in this data if any as it relates to the sdk?

Choose a reason for hiding this comment

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

Change made to literal string WebAuthn
Note for Kian: This means that the passed _type must be exactly that. If for whatever reason we want to pass in anything, then change it back to string. If we want to change it to discrete values then change it to 'X|Y|Z'

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

Choose a reason for hiding this comment

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

I see that _type is still string. Did you forget to push these changes?


// Check if the browser supports conditional mediation
if (typeof PublicKeyCredential.isConditionalMediationAvailable === 'function') {
return await PublicKeyCredential.isConditionalMediationAvailable();
Copy link
Contributor

Choose a reason for hiding this comment

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

This method looks like it can throw so we may need to handle the error in case it does.

[Exceptions](https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential/isConditionalMediationAvailable_static#exceptions)
The returned [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) may be rejected with the following values:


SecurityError [DOMException](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
The RP domain is not valid.

Otherwise we may leave the auth state in a failure and unrecoverable error.

Choose a reason for hiding this comment

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

Updated.

_action: 'webauthn_authentication',
challenge: 'JEisuqkVMhI490jM0/iEgrRz+j94OoGc7gdY4gYicSk=',
allowCredentials: '',
_allowCredentials: [],
Copy link
Contributor

Choose a reason for hiding this comment

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

So AM now sends both of these values? we need to take precedence on the array I assume because it can list the possible passkeys?

Is this AM being backwards compatible with the older allowCredentials?

Copy link

@KMForgeRock KMForgeRock Jan 9, 2026

Choose a reason for hiding this comment

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

They are effectively identical as AM supports both for as you mentioned backwards compatibility. If you feel like this answers your question, give me a should and I can resolve this thread.

Choose a reason for hiding this comment

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

Commend addressed

*
* // For conditional UI to work in the browser, add autocomplete="webauthn"
* // to your username input field:
* // <input type="text" name="username" autocomplete="webauthn" />
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if this isn't provided and the promise is hanging?

Choose a reason for hiding this comment

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

I've got a fix in based on code I've seen elsewhere in the code base. Feel free to correct me if I've mssed up.

Choose a reason for hiding this comment

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

Kian note:
There is a current question in my fix if after timeout we shout throw an exception or just return false. Either way we should make it so the admin can specify a timeout time (have a default just incase).
Throwing errors seems to be what we do in the SDK

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, we throw in cases where the continuation is not possible, but in this case, we can consider this progressive enhancement, yeah? In other words, "conditional UI" isn't necessary for the basic function of WebAuthn, so we don't need to throw.

Copy link
Contributor

Choose a reason for hiding this comment

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

@cerebrl it's possible that we haven't really "started" webauthn though. This flow can be activated before a username or password has entered, so the fallback to "webauthn" is unlikely because usually you need to progress to that point.

Right?

*/
public static async getAuthenticationCredential(
options: PublicKeyCredentialRequestOptions,
useConditionalUI = false,
Copy link
Contributor

Choose a reason for hiding this comment

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

as per the hanging promise comment, we may need to use an abort signal here to allow developers to abort the promise if the passkey option isn't there?

I may be not following this correctly, so please correct me where I'm wrong, but if a developer is using autofill ui, but fails to add the html correctly, can't we end up in a hanging promise state?

Choose a reason for hiding this comment

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

Also abort signal may be the right path, we would have to figure out how to do it.

try{
return withTimeout(PublicKeyCredential.isConditionalMediationAvailable(), ONE_SECOND)
} catch {
throw new Error('Error determining conditional mediation support');
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we throw here, or just return false? If this feature isn't supported, we can just fall back to "normal" behavior, yeah?

Choose a reason for hiding this comment

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

Definitely feels like there is no "right" answer. I think we (Liskov) will discuss this as a team and then go forward with that, if that sounds good to oyu.

Copy link

@KMForgeRock KMForgeRock Jan 14, 2026

Choose a reason for hiding this comment

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

Talking to @george-bafaloukas-forgerock it may be worth keeping the ticket open to test when all other matters have been resolved, then we can fully test and make sure nothing it broken.

*
* // For conditional UI to work in the browser, add autocomplete="webauthn"
* // to your username input field:
* // <input type="text" name="username" autocomplete="webauthn" />
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, we throw in cases where the continuation is not possible, but in this case, we can consider this progressive enhancement, yeah? In other words, "conditional UI" isn't necessary for the basic function of WebAuthn, so we don't need to throw.

credentialRequestOptions.mediation = 'conditional' as CredentialMediationRequirement;
} else {
// eslint-disable-next-line no-console
console.warn('Conditional UI was requested, but is not supported by this browser.');
Copy link
Contributor

Choose a reason for hiding this comment

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

FRLogger itself handles the console log, so you can remove the duplicate console.warn statement.

}

interface WebAuthnAuthenticationMetadata {
_action?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

You mention that this change is done, but I still see string as the value for _action. Am I missing something?

userVerification: UserVerificationType;
conditionalWebAuthn?: boolean;
extensions?: Record<string, unknown>;
_type?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that _type is still string. Did you forget to push these changes?

@ryanbas21
Copy link
Contributor

Also please add a changeset. @cameronwhitworthforgerock


// Add conditional mediation if requested and supported
if (useConditionalUI) {
const isConditionalSupported = await this.isConditionalUISupported();

Choose a reason for hiding this comment

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

This won't hang as the timeout was added above.

}
}

const credential = await navigator.credentials.get(credentialRequestOptions);

Choose a reason for hiding this comment

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

This is for under discussion about hanging

Choose a reason for hiding this comment

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

Talking to @george-bafaloukas-forgerock this should already be handled by the browser as it is a variable in credentialRequestOptions

Choose a reason for hiding this comment

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

I've double checked and timeout is passed in.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants