nextcloud / nextcloud/twofactor_webauthn

Security key login fails with DataCloneError: Proxy object could not be cloned

Open Beginner friendly
#1,052 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
59
Forks
11
Avg merge
1d 10h
Merged PRs (30d)
16

Description

Steps to reproduce
  1. Register a security key as a second factor
  2. Log out, log in, pick the security key
  3. Nothing happens and the browser console shows the error below
Actual behaviour
[ERROR] twofactor_webauthn: challenge failed
Object { app: "twofactor_webauthn", uid: "…", level: 2, error: DOMException }
error: DOMException: Proxy object could not be cloned.
Cause

Challenge.vue passes credentialRequestOptions straight into startAuthentication():

authResponse = await startAuthentication({
    optionsJSON: this.credentialRequestOptions,
})

The value arrives from the Pinia store, so under Vue 3 it is a reactive Proxy. startAuthentication() forwards it to navigator.credentials.get(), which structured-clones its argument — and a Proxy cannot be cloned.

The frontend moved to Vue 3 in 2.2.0, which is when this would have started.

Suggested fix

Unwrap before the call:

import { toRaw } from 'vue'

authResponse = await startAuthentication({
    optionsJSON: structuredClone(toRaw(this.credentialRequestOptions)),
})

optionsJSON is a PublicKeyCredentialRequestOptionsJSON, so it holds JSON values only — the challenge and credential IDs are base64url strings, not ArrayBuffers. A deep clone loses nothing.

What I verified

I patched the built bundle on my instance:

ij({optionsJSON:n.credentialRequestOptions})
→ ij({optionsJSON:JSON.parse(JSON.stringify(n.credentialRequestOptions))})

Security key login works after that.

Registration takes the same code path in login-setup.js and settings.js, but there the options come straight from the server response, so no Proxy is involved and I saw no failure.

Versions
  • twofactor_webauthn 2.7.0
  • Nextcloud 34.0.3
  • Firefox

This does not hit every user — whether it fails depends on how strictly the browser enforces structured cloning on the argument, so a Chromium test would be worth adding to this report.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Challenge.vue at the startAuthentication() call and inspect how credentialRequestOptions is passed to the browser credential API. Confirm the request options are no longer a reactive Proxy, then verify that security key login completes without the DataCloneError; login-setup.js and settings.js provide the related registration paths for comparison.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
authentication
Issue type
Bug
Difficulty
1/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
90/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.