WebAuthn signing validation failing with 'Response did not match the challenge'

I’ve been looking to set up WebAuthn support in my PrivacyIDEA (3.8.1) install. To the best of my knowledge, I’ve configured everything properly, but when I attempt POST the WebAuthn sign response /validate/check endpoint, PrivacyIDEA responds with 'Response did not match the challenge.

Here are the policies I have configured:

{
    "action": {
        "webauthn_relying_party_id": "local.gaughan.ie",
        "webauthn_relying_party_name": "Keith Gaughan",
    },
    "active": True,
    "name": "enrollment",
    "priority": 1,
    "realm": ["users"],
    "resolver": ["LDAP"],
    "scope": "enrollment",
}

{
    "action": {
        "auditlog": True,
        "delete": True,
        "disable": True,
        "enrollU2F": True,
        "enrollWEBAUTHN": True,
        "reset": True,
        "revoke": True,
        "setdescription": True,
    },
    "active": True,
    "name": "selfservice",
    "priority": 1,
    "realm": ["users"],
    "resolver": ["LDAP"],
    "scope": "user",
}

{
    "action": {
        "u2f_facets": "pi.local.gaughan.ie auth.local.gaughan.ie",
        "webauthn_allowed_transports": "usb",
    },
    "active": True,
    "name": "sso",
    "priority": 1,
    "scope": "authentication",
}

With these in place, I’m able to enroll the token without any issues, but when I attempt to use the token, it fails.

This gist contains some Python source code for a small webapp to do the WebAuthn flow along with some requests and responses with anything sensitive redacted: webauthn.py · GitHub - it uses webauthn-client to do the heavy lifting in the browser, and the webapp behaves almost identically to the Node app in the webauthn-demo repo.

What’s particularly confusing is that if I register the Yubikey as a U2F token, it doesn’t have this issue at all, and when I try it with Yubico’s demo app (Yubico demo website) it works.

I’m obviously doing something wrong here, but I can’t for the life of me tell why.

Hi, have you checked that your origin is correct, i.e. matches the one that the token was created for?
The hostname in the browser should probably not be an ip.

Definitely. Both PI and the web app for triggering the signing request in the browser are running on hostnames under local.gaughan.ie, with the former running on pi.local.gaughan.ie and the web app running on auth.local.gaughan.ie. I’ve even checked the client data sent back, and it looks fine. The token is enrolled under pi.local.gaughan.ie.

Try to add “origin” with your origin to your headers that you send to /validate/check

That did the trick! Thanks! It’s not mentioned in the documentation, so I’ll open a PR to document it. I would’ve thought that the contents of the the clientdata field would be enough. That was driving me crazy trying to find out what was going on, so thanks again!