duo-labs / py_webauthn

Pythonic WebAuthn 🐍
https://duo-labs.github.io/py_webauthn
BSD 3-Clause "New" or "Revised" License
856 stars 171 forks source link

Could not decode CBOR data #229

Closed akhilv77 closed 6 days ago

akhilv77 commented 1 month ago

I am trying to authenticate the passkey login

Error: ``` { "data": { "loginPasskey": { "success": false, "token": null, "expires_in": null, "error": "Could not decode CBOR data", "user": null, "__typename": "SignInResponse" } } }


here is my function to authenticate

async def login_passkey(_, info, params):
    await prisma.connect()
    try:
        # Extract parameters
        credential_id = params.get('id')
        credential_response = params.get('response')

        if not credential_id or not credential_response:
            return {"success": False, "error": "Missing credential information"}

        # Fetch the passkey associated with the provided credential_id
        passkey = await prisma.passkey.find_unique(
            where={"credential_id": credential_id}
        )

        if not passkey:
            return {"success": False, "error": "Invalid credential"}

        # Extract necessary data for verification
        client_data_json = credential_response.get('clientDataJSON')
        authenticator_data = credential_response.get('authenticatorData')
        signature = credential_response.get('signature')
        user_handle = credential_response.get('userHandle')

        if not client_data_json:
            return {"success": False, "error": "Missing clientDataJSON"}
        if not authenticator_data:
            return {"success": False, "error": "Missing authenticatorData"}
        if not signature:
            return {"success": False, "error": "Missing signature"}
        if not user_handle:
            return {"success": False, "error": "Missing userHandle"}

        # Convert base64url strings to bytes
        challenge_bytes = base64url_to_bytes(passkey.challenge)
        public_key_bytes = base64url_to_bytes(passkey.credential_public_key)

        # Verify the passkey login using WebAuthn utilities
        verification_result = verify_authentication_response(
            credential={
                'id': credential_id,
                'rawId': credential_id,
                'response': {
                    'authenticatorData': authenticator_data,
                    'clientDataJSON': client_data_json,
                    'signature': signature,
                    'userHandle': user_handle,
                },
                'type': 'public-key',
            },
            credential_public_key=public_key_bytes,
            expected_challenge=challenge_bytes,
            credential_current_sign_count=passkey.sign_count or 0,
            expected_rp_id="localhost",
            expected_origin="http://localhost:3000",
            require_user_verification=True,
        )

        if not verification_result["success"]:
            return {"success": False, "error": "Passkey verification failed"}

        # Update the sign_count for the credential
        await prisma.passkey.update(
            where={"id": passkey.id},
            data={"sign_count": verification_result["new_sign_count"]}
        )

        user = await prisma.user.find_unique(
            where={"id": passkey.user_id},
            include={"user_preferences": True}
        )

        token, expiration = generate_token(user)

        return {
            "success": True,
            "token": token,
            "expires_in": expiration.isoformat(),
            "user": user
        }
    except Exception as e:
        # Log the error for debugging purposes
        print(f"Error during passkey login: {e}")
        return {"success": False, "error": str(e)}
    finally:
        await prisma.disconnect()

        here is the payload from client

{ "params": { "id": "teEZtZ_hZz4jFYaVVmCIulH6qS4", "rawId": "teEZtZ_hZz4jFYaVVmCIulH6qS4", "type": "public-key", "response": { "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA", "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiNGZjYjk2Mzk0OTFlYTFmN2NhYmM5MGU4NDU1YzU3YjM4NGI5MTg2MTZjOGJkYmYxZjc5YjkyMWI3ZGVmMWZmZCIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0", "signature": "MEUCIQCrVV0Eo6dNhvLfiloL0QohJXhUttOaVFHZrAisW6XK7AIgIxAO0yQFARFlQ2AnSf3lkxUYVix0oWbzWz370r3XNMo", "userHandle": "ODBlYTM5MTAtMTRhNC00YTBjLWEyM2MtZTIwNDg2ZDRiZGYz" } } }

akhilv77 commented 1 week ago

please respond if anyone have a solution for it.

MasterKale commented 6 days ago

Hello @akhilv77 can you please also provide the base64url string value of passkey.credential_public_key? I need that to have any chance of reproducing this locally.

akhilv77 commented 6 days ago

@MasterKale here is my public key - cS_aTyvread-0Qvx188PiRwq_co

payload from front end:

{ "id": "cS_aTyvread-0Qvx188PiRwq_co", "rawId": "cS_aTyvread-0Qvx188PiRwq_co", "type": "public-key", "response": { "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA", "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiN2YzNmFmZjdhZTg0YzllZGVlNmMyYjhmOGZhM2NiYjI2NmExODNkOTRiNDA4YTY5N2ZlMzVkM2QwZmMzOGQzYyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0", "signature": "MEUCIQDfe7FXZRXeSdwBof_9giQYxh3f_4ua5MuUmaygEIpi_wIgMQcjDOfTN4hyVmN2ffNhnACAF094GleihpizDBlxxHQ", "userHandle": "YWRiY2ZjYTUtM2M3YS00MWM4LTkxZjAtNWQ0NjFhNDU1Yzhm" } }

akhilv77 commented 6 days ago

@MasterKale I identified the issue: I was storing the incorrect public key. Upon reviewing the logic, I realized that the public key from the registration response needed to be used. After making this adjustment, the process worked correctly.

Thank you for your prompt response.