{"record":{"id":"b2564e7121bec3c9","repo":"benweet/stackedit","slug":"google-account-id-not-expected","errorCode":null,"errorMessage":"Google account ID not expected.","messagePattern":"Google account ID not expected\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/providers/helpers/googleHelper.js","lineNumber":146,"sourceCode":"      silent,\n    );\n\n    // Call the token info endpoint\n    const { body } = await networkSvc.request({\n      method: 'POST',\n      url: 'https://www.googleapis.com/oauth2/v3/tokeninfo',\n      params: {\n        access_token: accessToken,\n      },\n    }, true);\n\n    // Check the returned client ID consistency\n    if (body.aud !== clientId) {\n      throw new Error('Client ID inconsistent.');\n    }\n    // Check the returned sub consistency\n    if (sub && `${body.sub}` !== sub) {\n      throw new Error('Google account ID not expected.');\n    }\n\n    // Build token object including scopes and sub\n    const existingToken = store.getters['data/googleTokensBySub'][body.sub];\n    const token = {\n      scopes,\n      accessToken,\n      expiresOn: Date.now() + (expiresIn * 1000),\n      idToken,\n      sub: body.sub,\n      name: (existingToken || {}).name || 'Someone',\n      isLogin: !store.getters['workspace/mainWorkspaceToken'] &&\n        scopes.includes('https://www.googleapis.com/auth/drive.appdata'),\n      isSponsor: false,\n      isDrive: scopes.includes('https://www.googleapis.com/auth/drive') ||\n        scopes.includes('https://www.googleapis.com/auth/drive.file'),\n      isBlogger: scopes.includes('https://www.googleapis.com/auth/blogger'),\n      isPhotos: scopes.includes('https://www.googleapis.com/auth/photos'),","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/benweet/stackedit/blob/6dce2a5e36b755a0c244522b48a06c91a2df0f59/src/services/providers/helpers/googleHelper.js#L128-L164","documentation":"After validating the ID token's audience, startOauth2 compares the token's 'sub' claim (Google's stable user ID) against an optional expected sub. When an expected sub was supplied and `${body.sub}` !== sub, it throws 'Google account ID not expected.' — the signed-in Google account differs from the one already associated with this entry.","triggerScenarios":"signin, token (callback), or refreshToken where the Google account completing the flow (or holding the refresh token) has a different sub than the stored one — e.g. switching Google accounts in the browser, or a refresh token belonging to another user.","commonSituations":"Multiple Google accounts logged into the browser; account selector picking the wrong profile; refresh after the original account was suspended/deleted; stored sub from a previous database copied across workspaces.","solutions":["Reconnect using the originally intended Google account (use login_hint/authuser or a private window to force account choice).","Clear the stored Google token/sub entry and re-run sign-in to bind the current account.","If the original account was deleted, migrate the workspace data before reconnecting.","Verify the expected sub passed into startOauth2 is the correct stored value and not from another account's record."],"exampleFix":"// before\nconst token = await googleHelper.refreshToken(refreshToken, sub);\n// after\ntry {\n  const token = await googleHelper.refreshToken(refreshToken, sub);\n} catch (e) {\n  if (e.message === 'Google account ID not expected.') {\n    // wrong Google account; force a fresh sign-in\n    const token = await googleHelper.signin(newSubHint);\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// decode the ID token client-side before validating\nconst payload = JSON.parse(atob(idToken.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')));\nif (expectedSub && String(payload.sub) !== expectedSub) {\n  // wrong Google account: restart sign-in with account chooser (prompt=select_account)\n}","typeGuard":"function isExpectedGoogleSub(idTokenBody, expectedSub) {\n  return !expectedSub || `${idTokenBody.sub}` === expectedSub;\n}","tryCatchPattern":"try {\n  const token = await googleHelper.startOauth2(code, sub, clientId);\n} catch (err) {\n  if (err.message === 'Google account ID not expected.') {\n    // prompt user to pick the originally linked Google account or clear the binding\n  } else throw err;\n}","preventionTips":["Add prompt=select_account to the Google auth URL so users consciously pick the account","Use login_hint with the stored account email when refreshing","Clear stored sub/token entries when unlinking an account","Check the account email in the token payload and show it to the user before binding"],"tags":["google","oauth2","account-mismatch","sub-claim","token"],"backgroundTag":"oauth-account-mismatch","analyzedSha":"6dce2a5e36b755a0c244522b48a06c91a2df0f59","analyzedAt":"2026-09-01T00:49:23.866Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}