vrana/adminer · error · Error
The passkey does not support storing passwords.
Error message
The passkey does not support storing passwords.
What it means
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.'
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.
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
// Before invoking the passkey login/decryption flow:
function prfSupportedInGet() {
return typeof PublicKeyCredential !== 'undefined' &&
typeof navigator.credentials !== 'undefined';
}
// Note: authenticator PRF support cannot be fully known before an assertion;
// guard the extension result itself:
function prfResultsAvailable(credential) {
const prf = credential.getClientExtensionResults().prf;
return !!(prf && prf.results && prf.results.first);
} Type guard
function hasPrfResults(credential) {
const prf = credential.getClientExtensionResults().prf;
return typeof prf === 'object' && prf !== null &&
typeof prf.results === 'object' && prf.results !== null &&
prf.results.first instanceof ArrayBuffer;
} Try / catch
try {
const { key, id, password } = await passkeyGet(id);
// proceed with decrypted accounts
} catch (err) {
if (err.message === 'The passkey does not support storing passwords.') {
showFallbackPasswordPrompt(); // ask user for a PRF-capable passkey
} else {
throw err;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
AI-assisted analysis of vrana/adminer@5106d54c84 (2026-09-13).
Data as JSON: /api/errors/8430d5909500632c.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/login-passkey.php:168
crypto.subtle.importKey('raw', prf, {name: 'AES-GCM'}, false, ['encrypt', 'decrypt']),
crypto.subtle.digest('SHA-256', password).then(passkeyHex),
]).then(derived => ({key: derived[0], id: passkeyUrl(rawId), password: derived[1]}));
}
/** Get the encryption key from a passkey
* @param {string} id base64url encoded ID of the passkey, empty string to use any
* @return {Promise} resolves to {key: CryptoKey, id: string, password: string}
*/
function passkeyGet(id) {
return navigator.credentials.get({publicKey: {
challenge: passkeyRandom(32), // the assertion is not verified, the key is verified by decrypting the accounts
allowCredentials: (id ? [{type: 'public-key', id: passkeyBytes(id)}] : []),
userVerification: 'required', // the PRF extension requires it
extensions: {prf: {eval: {first: passkeySalt}}},
}}).then(credential => {
const prf = credential.getClientExtensionResults().prf;
if (!prf || !prf.results) {
throw new Error(passkeyLang.unsupported);
}
return passkeyDerive(prf.results.first, credential.rawId);
});
}
/** Create a new passkey
* @return {Promise} resolves to {key: CryptoKey, id: string, password: string}
*/
function passkeyCreate() {
return navigator.credentials.create({publicKey: {
challenge: passkeyRandom(32),
rp: {name: 'Adminer'},
user: {id: passkeyRandom(16), name: 'Adminer', displayName: 'Adminer'},
pubKeyCredParams: [{type: 'public-key', alg: -7}, {type: 'public-key', alg: -257}],
authenticatorSelection: {residentKey: 'preferred', userVerification: 'required'},
extensions: {prf: {eval: {first: passkeySalt}}},
}}).then(credential => {
const prf = credential.getClientExtensionResults().prf;View on GitHub (pinned to 5106d54c84)