Troubleshooting
This page provides solutions to common issues you may encounter when integrating or using the FIDO2 Web SDK.
SDK not loaded
Symptoms:
window.com$thalesgroup$gemalto$fido2$webisundefined- Error: "Cannot read property 'initSdk' of undefined"
Causes:
- Script tag pointing to wrong URL
- Network error preventing bundle download
- SDK loading after your application code executes
- CORS blocking the bundle
Solutions:
-
Verify the script tag URL is correct:
html <script src="/lib/idc-fido2-web-js-lib-bundle.js"></script> -
Open browser DevTools → Network tab, reload the page, and verify the bundle file returns HTTP 200.
-
Ensure the SDK bundle tag appears before your application script tag:
html <script src="/lib/idc-fido2-web-js-lib-bundle.js"></script> <script src="/js/your-app.js"></script> -
If using
defer, ensure both scripts usedeferand the SDK appears first:html <script src="/lib/idc-fido2-web-js-lib-bundle.js" defer></script> <script defer> window.addEventListener('load', () => { if (window.com$thalesgroup$gemalto$fido2$web) { console.log('SDK loaded'); } }); </script>
SDK initialization fails
Symptoms:
initSdk()throws an error- Error code:
DBK-0010orDBK-0011
Causes:
- IndexedDB not available or disabled
- Storage quota exceeded
- Unsupported URL scheme (for example,
file://)
Solutions:
-
Verify IndexedDB is available:
javascript if (!window.indexedDB) { console.error('IndexedDB not supported'); } -
Verify the page is in a secure context:
javascript if (!window.isSecureContext) { console.error('Not in secure context — HTTPS required'); } -
Verify the URL scheme:
- Supported:
https://example.com,http://localhost:3000 - Not supported:
file:///path/to/page.html,http://example.com
- Supported:
-
Clear browser storage data and reload the page.
Registration fails
Symptoms:
webauthnCreateCred()throws an error- User is prompted but the operation fails
- Error:
NotAllowedErrororInvalidStateError
Causes:
- User cancelled the authenticator prompt
- Authenticator already registered
- No user gesture before the operation
- Invalid or malformed options from the server
Solutions:
-
Handle user cancellation:
javascript try { const credential = await webauthnCreateCred({ options }); } catch (error) { if (error.name === 'NotAllowedError') { showMessage('Please try again'); } } -
Handle already registered:
javascript if (error.name === 'InvalidStateError') { showMessage('This device is already registered. Use it to log in.'); } -
Ensure the function is called from a user gesture:
```javascript // Incorrect — no user gesture window.addEventListener('load', async () => { await webauthnCreateCred({ options }); // Will fail in most browsers });
// Correct — triggered by user action button.addEventListener('click', async () => { await webauthnCreateCred({ options }); // Works }); ```
-
Log and inspect the options to verify required fields are present:
javascript console.log('Attestation options:', JSON.stringify(attestationOptions, null, 2)); // Verify: challenge, rp.id, user.id, user.name, pubKeyCredParams
Authentication fails
Symptoms:
webauthnGetCred()throws an error- Error:
NotFoundErroror SDK errorDBK-0015
Causes:
- No credentials registered for this user
- Device Bound Key keypair not found
- Wrong user ID used
- IndexedDB data was cleared
Solutions:
-
Handle no credentials:
javascript if (error.name === 'NotFoundError') { redirectToRegistration(); } -
Handle missing Device Bound Key:
javascript if (error.code === 'DBK-0015') { showMessage('Please register on this device first'); } -
Verify
allowCredentialsin the server response includes at least one credential ID for the user.
HTTPS / secure context errors
Symptoms:
- Error:
SecurityError - WebAuthn operations fail immediately
Causes:
- Page served over HTTP (not HTTPS)
- Invalid SSL certificate
- Mixed content (HTTPS page loading HTTP resources)
Solutions:
- Use HTTPS in production and
http://localhostorhttp://127.0.0.1for development. - For development with a custom domain, use a local reverse proxy with SSL certificates, or tools such as
mkcert.
CORS errors
Symptoms:
- Bundle fails to load
- Console shows: "CORS policy blocked"
Causes:
- Bundle hosted on a different domain without CORS headers
Solutions:
-
Self-host the bundle on the same origin:
html <script src="/lib/idc-fido2-web-js-lib-bundle.js"></script> -
If hosting on a CDN, configure the appropriate CORS response headers:
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET
Device Bound Key issues
Symptoms:
- DBK signature verification fails on the server
- Error codes
DBK-0014orDBK-0015 - Different
dbkKidvalues between registration and authentication
Causes:
- IndexedDB cleared between registration and authentication
- Different browser or device used
- Incognito session ended
- Inconsistent user ID used across operations
Solutions:
-
Use the same user ID (same string, same base64url encoding) for all operations:
```javascript const userIdB64Url = base64urlEncode('user123');
// Registration const { dbkKid } = await getDeviceBoundKeyId(userIdB64Url);
// Authentication (same userId) const { dbkKid: dbkKid2 } = await getDeviceBoundKeyId(userIdB64Url); // dbkKid === dbkKid2 should be true ```
-
If IndexedDB was cleared, the user must re-register on this browser. Use
getDeviceBoundKeyId()to check DBK existence before attempting authentication. -
Warn users about incognito mode limitations: Device Bound Keys created in one incognito session are not available in subsequent sessions.
Auto-binding fallback not shown — window.confirm() suppressed
Symptoms:
registerDeviceBoundCredential()throwsWEB-0016immediately after assertion fails- No confirmation dialog was shown to the user
- Behaviour is inconsistent across calls or browsers
Cause:
The SDK's default fallback uses window.confirm(). Browsers can suppress this dialog silently (returning false without showing any UI) in the following situations:
- The same origin has shown the dialog repeatedly
- The browser is configured to suppress JavaScript dialogs (enterprise policy, kiosk mode)
- The page is in an iframe or WebView with restricted dialog permissions
- The browser is running in a headless or automated environment
When suppressed, the SDK receives false from the default confirmation and throws WEB-0016 as if the user declined — even though no dialog appeared.
Diagnosis:
// Quick check: does window.confirm() work in your environment?
const result = window.confirm('Test dialog');
console.log('confirm returned:', result);
// If this logs false without showing a dialog, window.confirm() is suppressed
Solution: Always provide a custom onFallbackConfirmation callback in production:
const result = await registerDeviceBoundCredential({
assertionReq,
attestationReq,
onFallbackConfirmation: () => new Promise((resolve) => {
showMyModal({
message: 'Your passkey was not found on this browser. Register a new one?',
onConfirm: () => resolve(true),
onCancel: () => resolve(false),
});
}),
});
Auto-binding registration validation errors
Symptoms:
registerDeviceBoundCredential()throwsWEB-0015,WEB-0012,WEB-0013, orWEB-0014- Auto-binding fails immediately without prompting the authenticator
| Code | Cause | Fix |
|---|---|---|
WEB-0015 |
assertionReq or attestationReq missing |
Verify both are returned by the backend dual-options endpoint |
WEB-0012 |
allowNewDbkBinding not set to true |
Set allowNewDbkBinding: true in assertionReq.extensions.thalesgroup_dbk_ext |
WEB-0013 |
allowedAlgorithms missing |
Add allowedAlgorithms: [-7] (or -35, -36) to the extension |
WEB-0014 |
allowedAlgorithms is an empty array |
Provide at least one COSE algorithm identifier |
Diagnosis:
// Inspect the dual options response
const body = await dualOptionsResponse.json();
console.log('assertionReq:', body.assertionReq);
console.log('attestationReq:', body.attestationReq);
const ext = body.assertionReq?.extensions?.thalesgroup_dbk_ext;
console.log('allowNewDbkBinding:', ext?.allowNewDbkBinding); // Must be true
console.log('allowedAlgorithms:', ext?.allowedAlgorithms); // Must be a non-empty array
Browser-specific issues
Windows — Windows Hello not prompting
- Verify Windows Hello is configured (Settings → Accounts → Sign-in options).
- Check that the browser has permission to access Windows Hello.
- Edge and Chrome have the best Windows Hello support.
macOS — Touch ID not prompting
- Verify Touch ID is enabled (System Preferences → Touch ID).
- Ensure the browser has accessibility permissions in System Preferences → Security & Privacy.
- Safari and Chrome have the best Touch ID support on macOS.
iOS — Face ID / Touch ID not prompting
- Ensure Safari is used. All browsers on iOS use Safari's WebKit engine.
- Check that Face ID or Touch ID is enabled in iOS Settings.
- Verify the page is served over HTTPS.
Linux — Security keys not recognized
- Add udev rules for security key access.
- Install
libfido2oru2f-hostpackages if required. - Some distributions require additional package installation before USB security keys are accessible.
Debugging tips
Inspect WebAuthn options
Log the options before and after SDK calls to verify the data flow:
console.log('Attestation options:', JSON.stringify(attestationOptions, null, 2));
const credential = await webauthnCreateCred({ options: attestationOptions });
console.log('Credential created:', JSON.stringify(credential, null, 2));
Inspect IndexedDB
Open browser DevTools → Application → Storage → IndexedDB. The SDK stores data under thalesgroup-fido2-sdk. You can inspect the stored HMAC seed and keypairs to verify that initialization succeeded.
Monitor network traffic
const originalFetch = window.fetch;
window.fetch = function(...args) {
console.log('[FETCH]', args[0]);
return originalFetch.apply(this, args).then(response => {
console.log('[RESPONSE]', args[0], response.status);
return response;
});
};
Frequently asked questions
Can I use the SDK in a Node.js application?
No. The SDK depends on browser APIs (WebAuthn, IndexedDB, Web Crypto API) and only works in a browser environment. For server-side FIDO2, use the FIDO2 Server API directly.
Does the SDK work in iframes?
WebAuthn operations in iframes require that the iframe shares the same origin as the parent, or that the parent has granted the appropriate permissions policy (publickey-credentials-create, publickey-credentials-get). This configuration is generally not recommended.
Can I test without a physical authenticator?
Yes. Modern browsers provide built-in platform authenticators (Windows Hello, Touch ID). Chrome DevTools also includes a Virtual Authenticator tool (DevTools → More tools → WebAuthn) for testing without hardware.
Why do I get different dbkKid values for the same user?
The dbkKid depends on the device, browser, Relying Party hostname (rpId), user ID, and IndexedDB persistence. If any of these change — including clearing browser data or switching to a new incognito session — a different dbkKid is computed.
What happens if the user clears their browser data?
Device Bound Keys stored in IndexedDB are lost. The user must re-register on this browser. Use getDeviceBoundKeyId() to detect this before attempting authentication.
Can users register multiple authenticators?
Yes. Users can register multiple authenticators. The FIDO2 Server manages multiple credentials per user. See the FIDO authentication APIs for admin operations on credentials.
Getting support
If the issue is not resolved by this guide:
- Identify the error code — see Error handling.
- Check Browser compatibility.
- Create a support ticket (FIDBACKEND project, Service Request type) and include:
- SDK version
- Browser name and version
- Operating system
- Error code and full error message
- Steps to reproduce
- Sanitized network logs and console output
- Sanitized server options (no sensitive data)