The vulnerability exists in the device authorization flow of the better-auth library, where a failure to properly bind a pending device authorization code to the initiating user allows for session hijacking. The analysis of the patch commit 6184ec3b40d40be050d46f2d46edcb7cbd6ee81c reveals the core issues.
-
Root Cause in deviceVerify: The deviceVerify function (handling GET /device) was responsible for verifying the user-provided code. In vulnerable versions, it only checked the code's validity but did not associate the authenticated user's session with that code. The patch adds logic to "claim" the code by updating the deviceCode record in the database with the current userId.
-
Exploitable Functions deviceApprove and deviceDeny: The deviceApprove (POST /device/approve) and deviceDeny (POST /device/deny) endpoints contained a flawed authorization check. The code checked for ownership only if a userId was already present on the device code record (if (deviceCodeRecord.userId && ...)). Since deviceVerify never set the userId, this check was always bypassed for pending codes. This allowed any authenticated user (an attacker) who knew a valid user_code to either approve the device for their own account (account takeover on the device) or deny it (denial of service).
-
The Fix: The patch corrects this by first making deviceVerify claim the code for the current user. Subsequently, it modifies deviceApprove and deviceDeny to throw an error if the code has not been claimed (!deviceCodeRecord.userId) or if the claiming user does not match the current user (deviceCodeRecord.userId !== session.user.id).
Therefore, the functions deviceApprove and deviceDeny are directly vulnerable as they process the malicious requests. The function is also included as its incorrect implementation is the root cause that enables the exploit. During an exploit, a profiler would show calls to or from the attacker's session.