feat: refactor PRF extension handling in credential options

This commit is contained in:
shuaiplus
2026-06-28 14:13:06 +08:00
parent 31cfd19b6b
commit 6a1a8357bf
+9 -16
View File
@@ -136,12 +136,12 @@ function withPrfExtension(
}; };
} }
function withoutPrfExtension<T extends PublicKeyCredentialCreationOptions | PublicKeyCredentialRequestOptions>(options: T): T { function withoutCreatePrfExtension(options: PublicKeyCredentialCreationOptions): PublicKeyCredentialCreationOptions {
const extensions = { ...(((options as any).extensions || {}) as Record<string, unknown>) }; const extensions = { ...(((options as any).extensions || {}) as Record<string, unknown>) };
delete extensions.prf; delete extensions.prf;
if (!Object.keys(extensions).length) { if (!Object.keys(extensions).length) {
const { extensions: _extensions, ...rest } = options as any; const { extensions: _extensions, ...rest } = options as any;
return rest as T; return rest as PublicKeyCredentialCreationOptions;
} }
return { return {
...options, ...options,
@@ -184,14 +184,6 @@ async function getPublicKeyCredentialWithPrf(
salt: Uint8Array, salt: Uint8Array,
credentialIds: string[] = [] credentialIds: string[] = []
): Promise<PublicKeyCredential> { ): Promise<PublicKeyCredential> {
const baseOptions = withoutPrfExtension(options);
if (!(await canRequestPrfExtension())) {
const credential = await navigator.credentials.get({ publicKey: baseOptions });
if (!(credential instanceof PublicKeyCredential)) {
throw new Error(t('txt_no_passkey_selected'));
}
return credential;
}
const attempts = credentialIds.length const attempts = credentialIds.length
? [ ? [
buildCredentialPrfExtension(salt, credentialIds), buildCredentialPrfExtension(salt, credentialIds),
@@ -202,7 +194,7 @@ async function getPublicKeyCredentialWithPrf(
for (let index = 0; index < attempts.length; index += 1) { for (let index = 0; index < attempts.length; index += 1) {
try { try {
const credential = await navigator.credentials.get({ const credential = await navigator.credentials.get({
publicKey: withPrfExtension(baseOptions, attempts[index]), publicKey: withPrfExtension(options, attempts[index]),
}); });
if (!(credential instanceof PublicKeyCredential)) { if (!(credential instanceof PublicKeyCredential)) {
throw new Error(t('txt_no_passkey_selected')); throw new Error(t('txt_no_passkey_selected'));
@@ -308,7 +300,8 @@ export async function createAccountPasskeyCredential(
if (!window.PublicKeyCredential || !navigator.credentials) { if (!window.PublicKeyCredential || !navigator.credentials) {
throw new Error(t('txt_passkey_browser_not_supported')); throw new Error(t('txt_passkey_browser_not_supported'));
} }
const nativeOptions = withoutPrfExtension(cloneCreationOptions(response.options)); const nativeOptions = cloneCreationOptions(response.options);
const noPrfOptions = withoutCreatePrfExtension(nativeOptions);
const createWithOptions = async (options: PublicKeyCredentialCreationOptions): Promise<PublicKeyCredential> => { const createWithOptions = async (options: PublicKeyCredentialCreationOptions): Promise<PublicKeyCredential> => {
const credential = await navigator.credentials.create({ publicKey: options }); const credential = await navigator.credentials.create({ publicKey: options });
if (!(credential instanceof PublicKeyCredential)) { if (!(credential instanceof PublicKeyCredential)) {
@@ -319,9 +312,9 @@ export async function createAccountPasskeyCredential(
let credential: PublicKeyCredential; let credential: PublicKeyCredential;
if (requestPrf && await canRequestPrfExtension()) { if (requestPrf && await canRequestPrfExtension()) {
const prfOptions: PublicKeyCredentialCreationOptions = { const prfOptions: PublicKeyCredentialCreationOptions = {
...nativeOptions, ...noPrfOptions,
extensions: { extensions: {
...((nativeOptions as any).extensions || {}), ...((noPrfOptions as any).extensions || {}),
prf: {}, prf: {},
} as any, } as any,
}; };
@@ -329,10 +322,10 @@ export async function createAccountPasskeyCredential(
credential = await createWithOptions(prfOptions); credential = await createWithOptions(prfOptions);
} catch (error) { } catch (error) {
if (!shouldRetryCreateWithoutPrf(error)) throw error; if (!shouldRetryCreateWithoutPrf(error)) throw error;
credential = await createWithOptions(nativeOptions); credential = await createWithOptions(noPrfOptions);
} }
} else { } else {
credential = await createWithOptions(nativeOptions); credential = await createWithOptions(noPrfOptions);
} }
if (!(credential instanceof PublicKeyCredential)) { if (!(credential instanceof PublicKeyCredential)) {
throw new Error(t('txt_no_passkey_created')); throw new Error(t('txt_no_passkey_created'));