From 387678a771c91ab70bb2e6eb493382c41265c63d Mon Sep 17 00:00:00 2001 From: jvoisin Date: Tue, 14 Apr 2026 22:28:06 +0200 Subject: [PATCH] Prevent OAuth relinking. When an OAuth provider_user_id is already linked to User A, and User B authenticates with the same OAuth identity, User B is silently logged in as User A. This is by design for single-user OAuth, but in a multi-user environment it means: if an attacker gains access to the same OAuth provider account (e.g., a shared GitHub org account, or by compromising the OAuth provider), they can log in as the linked Calibre-Web user with no password needed. --- cps/oauth_bb.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cps/oauth_bb.py b/cps/oauth_bb.py index 7ec813bdc..0ac47c391 100644 --- a/cps/oauth_bb.py +++ b/cps/oauth_bb.py @@ -134,6 +134,14 @@ def bind_oauth_or_register(provider_id, provider_user_id, redirect_url, provider oauth_entry = query.first() # already bind with user, just login if oauth_entry.user: + # If a user is already logged in and it's a different account, reject the link + # to prevent account takeover via shared OAuth identities + if current_user and current_user.is_authenticated and oauth_entry.user_id != current_user.id: + flash(_("This %(oauth)s account is already linked to a different user", + oauth=provider_name), category="error") + log.warning("User %s tried to link OAuth account already bound to user %s", + current_user.id, oauth_entry.user_id) + return redirect(url_for('web.profile')) login_user(oauth_entry.user) log.debug("You are now logged in as: '%s'", oauth_entry.user.name) flash(_("Success! You are now logged in as: %(nickname)s", nickname=oauth_entry.user.name),