{"record":{"id":"03bbcae95c62b779","repo":"dotnet/aspnetcore","slug":"some-passkey-features-are-missing-please-update-y","errorCode":null,"errorMessage":"Some passkey features are missing. Please update your browser.","messagePattern":"Some passkey features are missing\\. Please update your browser\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/Shared/PasskeySubmit.razor.js","lineNumber":67,"sourceCode":"        };\n\n        this.internals.form.addEventListener('submit', (event) => {\n            if (event.submitter?.name === '__passkeySubmit') {\n                event.preventDefault();\n                this.obtainAndSubmitCredential();\n            }\n        });\n\n        this.tryAutofillPasskey();\n    }\n\n    disconnectedCallback() {\n        this.abortController?.abort();\n    }\n\n    async obtainCredential(useConditionalMediation, signal) {\n        if (!browserSupportsPasskeys) {\n            throw new Error('Some passkey features are missing. Please update your browser.');\n        }\n\n        if (this.attrs.operation === 'Create') {\n            return await createCredential(signal);\n        } else if (this.attrs.operation === 'Request') {\n            const email = new FormData(this.internals.form).get(this.attrs.emailName);\n            const mediation = useConditionalMediation ? 'conditional' : undefined;\n            return await requestCredential(email, mediation, signal);\n        } else {\n            throw new Error(`Unknown passkey operation '${this.attrs.operation}'.`);\n        }\n    }\n\n    async obtainAndSubmitCredential(useConditionalMediation = false) {\n        this.abortController?.abort();\n        this.abortController = new AbortController();\n        const signal = this.abortController.signal;\n        const formData = new FormData();","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/Shared/PasskeySubmit.razor.js#L49-L85","documentation":"Thrown by obtainCredential when the module-level browserSupportsPasskeys constant is false. That constant checks that window.PublicKeyCredential exists AND that the static parseCreationOptionsFromJSON / parseRequestOptionsFromJSON methods are present — the latter require modern browsers (Chrome ~116+, Safari 16+, Firefox 122+). The browser lacks full WebAuthn conditional-meditation + JSON-parsing support.","triggerScenarios":"Calling obtainCredential (via obtainAndSubmitCredential, e.g. on form submit) in a browser where window.PublicKeyCredential is undefined, or where the parse*FromJSON static methods are missing. This includes all IE, old Safari (<16), old Chrome, and embedded WebViews.","commonSituations":"User on an older mobile WebView; enterprise-locked browsers with WebAuthn disabled; testing in headless browsers without virtual authenticators; deploying the passkey component to a population that includes legacy browsers without a fallback auth method.","solutions":["Detect browserSupportsPasskeys at component render time and hide/disable the passkey submit button instead of letting the user trigger the throw.","Render a fallback login mechanism (password, TOTP, email link) when browserSupportsPasskeys is false.","Guide the user to update their browser; the message itself recommends this.","For automated testing, use a virtual authenticator (e.g. Playwright/WebdriverIO virtual authenticator) so the WebAuthn surface is present."],"exampleFix":"// before\nif (!browserSupportsPasskeys) {\n  throw new Error('Some passkey features are missing. Please update your browser.');\n}\n\n// after — gate the UI on the same constant and render fallback\n// in the razor component:\n// <button type=\"submit\" disabled=\"@(!BrowserSupportsPasskeys)\">Use passkey</button>\n// @if (!BrowserSupportsPasskeys) { <PasswordLogin /> }","handlingStrategy":"type-guard","validationCode":"export function browserSupportsPasskeys(): boolean {\n  return typeof window !== 'undefined'\n    && typeof window.PublicKeyCredential !== 'undefined'\n    && typeof (window.PublicKeyCredential as any).parseCreationOptionsFromJSON === 'function'\n    && typeof (window.PublicKeyCredential as any).parseRequestOptionsFromJSON === 'function'\n    && typeof window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable === 'function';\n}","typeGuard":"function supportsPasskeys(g: typeof globalThis): g is typeof globalThis & {\n  PublicKeyCredential: typeof PublicKeyCredential & {\n    parseCreationOptionsFromJSON: Function;\n    parseRequestOptionsFromJSON: Function;\n  };\n} {\n  return typeof g.PublicKeyCredential !== 'undefined'\n    && typeof g.PublicKeyCredential.parseCreationOptionsFromJSON === 'function'\n    && typeof g.PublicKeyCredential.parseRequestOptionsFromJSON === 'function';\n}","tryCatchPattern":"if (!browserSupportsPasskeys()) {\n  renderFallbackAuth(); // never invoke obtainCredential\n} else {\n  try { await component.obtainAndSubmitCredential(); } catch (e) { /* ... */ }\n}","preventionTips":["Gate passkey UI on the same constant the JS uses.","Always offer a non-passkey fallback authentication path.","For automated tests, run under a browser/profile that exposes a virtual authenticator."],"tags":["browser-support","passkey","webauthn","feature-detection","blazor"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}