WebAuthn is a relatively new API for authentication, and it uses public key cryptography instead of something like passwords.

I am wondering if it is possible to use the cryptographic part for a different purpose, specifically creating digital signatures of documents in the browser.

The idea is to find a way to have a user sign a document in the browser in a way that the server can't fake or manipulate. So the web application itself must never know the private key that is used for signing, which is the case for WebAuthn as far as I understand.

So if instead of the random challenge that WebAuthn expects I'd send the contents of a document I want the user to sign, I should get back a cryptographically signed hash of my document, if I understand the explanation on MDN correctly.

In the end I should have a digital signature that could be used to prove that this document was signed by a specific private key from a specific hardware token. And neither the web application inside the browser nor the server component ever saw the private key, and can't fake this signature (of course the application could compromise this by switching out the content before signing, but not later).

I couldn't find anything on someone using WebAuthn for this purpose, and anything I could find related to digital signatures in the browser was about stuff like Java applets that is simply not an option today anymore.

Is my idea sound in general? Or am I misunderstanding how WebAuthn works and it's simply not possible to use it in this way? Are there any weaknesses or flaws in this approach that I'm missing?

  • Wondering about the same. Were you able to find the answer to this? – Gopalakrishna Palem Aug 16 '19 at 0:32

Yes, this is possible

A security challenge for the Web Authentication API can by any byte array (of at least 16 bytes), and it will be signed by the clients secure private key. It can therefore also be used to sign documents or messages of all types by passing the document as a PublicKeyCredentialRequestOptions.challenge in the call to navigator.credentials.get().

The PublicKeyCredential returned by the call contains a response property, which contains a signature property which is the signature you are looking for.

The example code from developer.mozilla.com slightly modified to fit your use-case:

fetch(/* get file from server */).then(response => response.arrayBuffer()).then(fileFromServer => {
    const options = {
        challenge: fileFromServer,
        rp: {
            name: "Example CORP",
            id  : "login.example.com"
        user: {
            id: userId,
            name: "jdoe@example.com",
            displayName: "John Doe"
        pubKeyCredParams: [
                type: "public-key",
                alg: -7

    navigator.credentials.get({  publickey: options })
        .then(function (pubKeyCredential) {
            const signature = pubKeyCredential.response.signature

            fetch(/* Send signature to server */)
    }).catch(function (err) {
      // Deal with any error
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.