demo · v137
SPC Error Probe with User Activation
The v137 rename (SecurityError → NotAllowedError) only fires on the missing-activation path. This probe adds the third lane from the spec matrix: credentials.create({ payment }) called with a fresh user gesture. Both pre-v137 and v137+ agree here: the result is either a credential or a NotAllowedError — never a SecurityError. Run both lanes to confirm activation is the key variable.
| Scenario | Spec outcome | Pre-v137 | v137+ | |
|---|---|---|---|---|
| same-origin, no activation | NotAllowedError | NotAllowedError | NotAllowedError | |
| cross-origin iframe, no activation | NotAllowedError | SecurityError ← bug | NotAllowedError ✓ | v137 fix |
| cross-origin iframe, with activation | resolves / NotAllowedError (dismiss) | NotAllowedError | NotAllowedError | this demo |
Lane A — no user activation
Runs immediately on click — the handler itself consumes the gesture, so create() is called without an associated activation. This is the path the v137 rename targets.
Lane B — with fresh user activation new lane
Your click IS the user gesture. create() is called synchronously inside the click handler with a fresh activation. Dismiss the browser dialog — you will always see NotAllowedError, never SecurityError.
// Lane A — no activation (the v137 fix targets this path):
// cross-origin iframe pre-v137: SecurityError ← wrong
// cross-origin iframe v137+: NotAllowedError ← correct
// same-origin (any version): NotAllowedError
//
// Lane B — with activation (this demo):
// any origin, any version: resolves | NotAllowedError (user dismiss)
// → SecurityError NEVER fires on the with-activation path
//
// Key takeaway: if your error handler checks `e.name === 'SecurityError'`
// you were only catching the cross-origin + no-activation path.
// Upgrade to NotAllowedError to handle all affected branches:
//
try {
const cred = await navigator.credentials.create({ publicKey });
} catch (e) {
if (e instanceof DOMException && e.name === 'NotAllowedError') {
handleDenied(); // handles both old SecurityError path and all NotAllowedError paths
}
}