Skip to content

fix(account): bind managed group forwarding key UserID to the canonical member address - #527

Open
SashaMIT wants to merge 1 commit into
ProtonMail:mainfrom
SashaMIT:fix/group-forward-userid
Open

fix(account): bind managed group forwarding key UserID to the canonical member address#527
SashaMIT wants to merge 1 commit into
ProtonMail:mainfrom
SashaMIT:fix/group-forward-userid

Conversation

@SashaMIT

@SashaMIT SashaMIT commented Aug 7, 2026

Copy link
Copy Markdown

Problem

Since 332003c, addGroupMember derives the forwarded key's UserID from the email embedded in the member's own primary public key:

const Email = getEmailFromKey(forwardeePublicKey) ?? email;
const userIDsForForwardeeKey = [{ email: Email, name: Email }];

The two internal branches diverge in what happens when that embedded email does not match the address being added:

  • Private / other-org path: issues an ActivationToken, and acceptGroupInvitation explicitly re-checks extractedEmail !== forwardeeEmail and calls cloneKeyAndChangeUserIDs to rebind the key. Self-healing.
  • Managed path (org key held): forwards a ready-to-use key with no activation step, so nothing ever corrects a mismatched UserID. The persisted group-member key can assert an email other than the address it is actually bound to, which surfaces in clients, audit views, and anywhere the key UserID is taken as identity.

Fix

On the managed path only, pass the canonical member address as the UserID — the address the key is actually issued for:

getInternalParameters(forwarderKey.privateKey, [{ email: canonicalEmail, name: canonicalEmail }], decryptedToken)

Applied to both variants of the flow (addGroupMemberApi and addGroupMemberKeysApi). The private path is intentionally unchanged: its activation flow performs the rebind, and preserving the key's own UserID there avoids a behavior change ahead of it.

Verification

tsc --noEmit on the edited file: identical diagnostic set before/after (17 pre-existing module-resolution errors from a shallow checkout without workspace install; zero errors attributable to this change). The diff uses only in-scope identifiers.

Made with Cursor

…al member address

Commit 332003c hoisted the forwardee key UserID derivation
(getEmailFromKey(forwardeePublicKey) ?? email) so both internal branches
take the email embedded in the member's self-asserted primary key. The
private/other-org branch is self-healing: acceptGroupInvitation compares
the extracted email against the real forwardee address and calls
cloneKeyAndChangeUserIDs on mismatch. The managed branch has no
activation step and no equivalent correction, so a forwarded
ready-to-use key can be persisted whose UserID asserts an address other
than the one it is issued for.

Pass canonicalEmail as the UserID on the managed path (both the v1
addGroupMemberApi and v2 addGroupMemberKeysApi variants), matching what
the key is actually bound to. The private path is unchanged because its
activation flow performs the rebind.
@mmso
mmso force-pushed the main branch 2 times, most recently from d9d8f8f to 95ce65c Compare August 12, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant