Laragear / WebAuthn

Authenticate users with Passkeys: fingerprints, patterns and biometric data.
MIT License
295 stars 37 forks source link

[1.2.1] userHandle empty causing User is not owner of the stored credential. #47

Closed MordiSacks closed 6 months ago

MordiSacks commented 1 year ago

PHP & Platform

8.2.7 Ubuntu 22.04.2

Database

MariaDB 10.10.5

Laravel version

10.14.1

Browser

Google Chrome 114.0.5735.198

Have you done this?

Expectation

Not sure

Description

When logging in the response from the browser publicKeyCredential.response contains userHandle: ''

not sure why its empty but it ise (I checked on a few other site using webauthn, and it's empty there also).

However in the database the user_id field is populated,
In Laragear\WebAuthn\Assertion\Validator\Pipes\CheckCredentialIsForUser::validateId there is a check againest user_id on the credentials model, this throws an exception when handle is empty.

Reproduction

public function registerOpts(AttestationRequest $request): Responsable
{
    return $request
        ->secureRegistration()
        //->fastRegistration()
        //->userless()
        //->allowDuplicates()
        ->toCreate();
}

public function register(AttestedRequest $request): Response
{
    $request->save($request->only(['alias']));

    return response()->noContent();
}

public function loginOpts(AssertionRequest $request): Responsable
{
    /** @var User $user */
    $user = Auth::guard('web')->user();

    return $request
        ->secureLogin()
        ->toVerify($user->only(['email']));
}

public function login(Request $request, AssertionValidator $assertion): Response
{
    $credential = $assertion
        ->send(new AssertionValidation($request))
        ->thenReturn()
        ->credential;

    if (!$credential) return \response()->noContent(422);

    session(['webauthn' => true]);

    return response()->noContent();
}

Stack trace & logs

not applicable
MordiSacks commented 1 year ago

As a work around I did this

    public function login(Request $request, AssertionValidator $assertion): Response
    {
        /** @var User $user */
        $user = Auth::guard('web')->user();

        $key = $user->webAuthnCredentials()->whereKey($request->json('id'))->first();

        $request->json()->set('response', ['userHandle' => $key?->user_id ?? ''] + $request->json('response'));

        $credential = $assertion
            ->send(new AssertionValidation($request))
            ->thenReturn()
            ->credential;

        if (!$credential) return \response()->noContent(422);

        session(['webauthn' => true]);

        return response()->noContent();
    }

The user is available in this context as I'm using this package as 2FA, and the user reaches this page after password login However as I'm not fluent with webauthn spec, this might introduce a vulnerability, use under own discretion!

DarkGhostHunter commented 6 months ago

Fixed in #52