{"record":{"id":"8430d5909500632c","repo":"vrana/adminer","slug":"the-passkey-does-not-support-storing-passwords","errorCode":null,"errorMessage":"The passkey does not support storing passwords.","messagePattern":"The passkey does not support storing passwords\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/login-passkey.php","lineNumber":168,"sourceCode":"\t\tcrypto.subtle.importKey('raw', prf, {name: 'AES-GCM'}, false, ['encrypt', 'decrypt']),\n\t\tcrypto.subtle.digest('SHA-256', password).then(passkeyHex),\n\t]).then(derived => ({key: derived[0], id: passkeyUrl(rawId), password: derived[1]}));\n}\n\n/** Get the encryption key from a passkey\n* @param {string} id base64url encoded ID of the passkey, empty string to use any\n* @return {Promise} resolves to {key: CryptoKey, id: string, password: string}\n*/\nfunction passkeyGet(id) {\n\treturn navigator.credentials.get({publicKey: {\n\t\tchallenge: passkeyRandom(32), // the assertion is not verified, the key is verified by decrypting the accounts\n\t\tallowCredentials: (id ? [{type: 'public-key', id: passkeyBytes(id)}] : []),\n\t\tuserVerification: 'required', // the PRF extension requires it\n\t\textensions: {prf: {eval: {first: passkeySalt}}},\n\t}}).then(credential => {\n\t\tconst prf = credential.getClientExtensionResults().prf;\n\t\tif (!prf || !prf.results) {\n\t\t\tthrow new Error(passkeyLang.unsupported);\n\t\t}\n\t\treturn passkeyDerive(prf.results.first, credential.rawId);\n\t});\n}\n\n/** Create a new passkey\n* @return {Promise} resolves to {key: CryptoKey, id: string, password: string}\n*/\nfunction passkeyCreate() {\n\treturn navigator.credentials.create({publicKey: {\n\t\tchallenge: passkeyRandom(32),\n\t\trp: {name: 'Adminer'},\n\t\tuser: {id: passkeyRandom(16), name: 'Adminer', displayName: 'Adminer'},\n\t\tpubKeyCredParams: [{type: 'public-key', alg: -7}, {type: 'public-key', alg: -257}],\n\t\tauthenticatorSelection: {residentKey: 'preferred', userVerification: 'required'},\n\t\textensions: {prf: {eval: {first: passkeySalt}}},\n\t}}).then(credential => {\n\t\tconst prf = credential.getClientExtensionResults().prf;","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/vrana/adminer/blob/5106d54c848adf38ba846720422b86d1f3839972/plugins/login-passkey.php#L150-L186","documentation":"This error is thrown by the login-passkey plugin's passkeyGet() when, after a successful WebAuthn navigator.credentials.get() assertion, the credential's client extension results contain no usable 'prf' extension output (no prf object or no prf.results). The plugin relies on the WebAuthn PRF (pseudo-random function) extension to derive an encryption key and server passwords from the passkey, so an authenticator/browser that does not evaluate PRF cannot be used for password storage. The message text comes from passkeyLang.unsupported, which reads 'The passkey does not support storing passwords.'","triggerScenarios":"Calling passkeyGet() (via loginFormField's stored-password flow) with a passkey whose authenticator does not support or did not evaluate the prf extension: credential.getClientExtensionResults().prf is undefined, or prf.results is missing, even though the assertion itself succeeded.","commonSituations":"Authenticators without PRF support (many older security keys, Windows Hello on some versions, some phone passkeys), browsers that do not implement the prf extension in credentials.get(), passkeys synced through providers that strip extensions (e.g. some iCloud/Google synced passkeys), or a UV-free/unsupported flow where userVerification was not enforced by the device.","solutions":["Use a different passkey/authenticator that supports the WebAuthn prf extension (e.g. a modern security key such as YubiKey firmware 5.2+, or a current browser platform authenticator).","Update the browser to a version supporting the prf extension in navigator.credentials (Chrome 117+/Edge 117+, Firefox 119+, Safari 18+).","Set up the passkey in an environment known to evaluate PRF, then retry the login/decryption flow with that passkey.","If PRF cannot be enabled, stop using the passkey-based password storage feature and store server passwords outside the passkey-encrypted store."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// Before invoking the passkey login/decryption flow:\nfunction prfSupportedInGet() {\n  return typeof PublicKeyCredential !== 'undefined' &&\n    typeof navigator.credentials !== 'undefined';\n}\n// Note: authenticator PRF support cannot be fully known before an assertion;\n// guard the extension result itself:\nfunction prfResultsAvailable(credential) {\n  const prf = credential.getClientExtensionResults().prf;\n  return !!(prf && prf.results && prf.results.first);\n}","typeGuard":"function hasPrfResults(credential) {\n  const prf = credential.getClientExtensionResults().prf;\n  return typeof prf === 'object' && prf !== null &&\n    typeof prf.results === 'object' && prf.results !== null &&\n    prf.results.first instanceof ArrayBuffer;\n}","tryCatchPattern":"try {\n  const { key, id, password } = await passkeyGet(id);\n  // proceed with decrypted accounts\n} catch (err) {\n  if (err.message === 'The passkey does not support storing passwords.') {\n    showFallbackPasswordPrompt(); // ask user for a PRF-capable passkey\n  } else {\n    throw err;\n  }\n}","preventionTips":["Check PublicKeyCredential.getClientCapabilities?.().prf (or equivalent feature detection) before offering passkey password storage.","Prefer authenticators with documented PRF support (FIDO2 security keys firmware 5.2+, recent platform authenticators).","Keep browsers updated to versions implementing the prf extension in credentials.get().","Avoid relying on synced passkeys that may not preserve extension outputs across devices.","Provide a non-PRF fallback login path so users on unsupported devices are not locked out."],"tags":["webauthn","passkey","prf-extension","browser-compatibility"],"backgroundTag":"unsupported-operation","analyzedSha":"5106d54c848adf38ba846720422b86d1f3839972","analyzedAt":"2026-09-13T22:53:31.624Z","contentChangedAt":"2026-09-13T22:53:31.624Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}