odoo/odoo · error · InvalidAuthenticationResponse

id and raw_id were not equivalent

Error message

id and raw_id were not equivalent

What it means

WebAuthn authentication verification rejects a credential whose 'id' string is not the base64url encoding of its 'raw_id' bytes. The spec requires both to represent the same credential ID; a mismatch means the JSON was hand-built or mangled in transit.

Source

Thrown at addons/auth_passkey/_vendor/webauthn/authentication/verify_authentication_response.py:85

        - `credential_public_key`: The public key for the credential's ID as provided in a
          preceding authenticator registration ceremony.
        - `credential_current_sign_count`: The current known number of times the authenticator was
          used.
        - (optional) `require_user_verification`: Whether or not to require that the authenticator
          verified the user.

    Returns:
        Information about the authenticator

    Raises:
        `helpers.exceptions.InvalidAuthenticationResponse` if the response cannot be verified
    """
    if isinstance(credential, str) or isinstance(credential, dict):
        credential = parse_authentication_credential_json(credential)

    # FIDO-specific check
    if bytes_to_base64url(credential.raw_id) != credential.id:
        raise InvalidAuthenticationResponse("id and raw_id were not equivalent")

    # FIDO-specific check
    if credential.type != PublicKeyCredentialType.PUBLIC_KEY:
        raise InvalidAuthenticationResponse(
            f'Unexpected credential type "{credential.type}", expected "public-key"'
        )

    response = credential.response

    client_data_bytes = byteslike_to_bytes(response.client_data_json)
    authenticator_data_bytes = byteslike_to_bytes(response.authenticator_data)
    signature_bytes = byteslike_to_bytes(response.signature)

    client_data = parse_client_data_json(client_data_bytes)

    if client_data.type != ClientDataType.WEBAUTHN_GET:
        raise InvalidAuthenticationResponse(
            f'Unexpected client data type "{client_data.type}", expected "{ClientDataType.WEBAUTHN_GET}"'

View on GitHub (pinned to 1e661df964)

Solutions

  1. On the client, send PublicKeyCredentialJSON via credentials.toJSON() (native) or base64url-encode rawId so id === base64url(rawId)
  2. If building the dict server-side, derive id from raw_id: credential['id'] = base64url_to_bytes-style encoding of raw_id
  3. Log both values and confirm which side (client serialization vs server storage) re-encoded the ID

Example fix

// before: manual serialization
const body = { id: btoa(String.fromCharCode(...new Uint8Array(cred.rawId))), rawId: Array.from(new Uint8Array(cred.rawId)), ... };
// after: native toJSON
const body = cred.toJSON(); // id and rawId are consistent base64url
Defensive patterns

Strategy: validation

Validate before calling

import base64

def credential_ids_consistent(cred: dict) -> bool:
    try:
        raw = base64.urlsafe_b64decode(cred['rawId'] + '==')
        return base64.urlsafe_b64encode(raw).rstrip(b'=').decode() == cred['id']
    except Exception:
        return False

Type guard

def is_wellformed_assertion_json(cred) -> bool:
    return (
        isinstance(cred, dict)
        and isinstance(cred.get('id'), str)
        and isinstance(cred.get('rawId'), str)
        and isinstance(cred.get('response'), dict)
        and all(isinstance(cred['response'].get(k), str)
                for k in ('clientDataJSON', 'authenticatorData', 'signature'))
    )

Try / catch

from auth_passkey._vendor.webauthn.helpers.exceptions import InvalidAuthenticationResponse
try:
    result = verify_authentication_response(...)
except InvalidAuthenticationResponse as e:
    logger.warning('assertion rejected: %s', e)
    return HTTPResponse(400, 'Passkey verification failed')  # never leak internals to client

Prevention

When it happens

Trigger: Calling verify_authentication_response() with a credential dict/JSON where 'id' was re-encoded (e.g. base64 instead of base64url, padding added/stripped) or where raw_id bytes were decoded with the wrong charset. Happens when a frontend passes navigator.credentials.get() output through a JSON layer that re-encodes ArrayBuffers.

Common situations: Custom JS serializing the PublicKeyCredential manually and encoding rawId with btoa (base64 with +/ and padding) instead of base64url; a middleware or ORM that base64-decodes then re-encodes fields; passing a dict built from database-stored fields with inconsistent encodings.

Related errors


AI-assisted analysis of odoo/odoo@1e661df964 (2026-08-15). Data as JSON: /api/errors/c058f655f7990376. Report an issue: GitHub.