{"record":{"id":"294c88bd5779ddb3","repo":"calcom/cal.diy","slug":"you-must-be-logged-in-to-do-this-294c88","errorCode":null,"errorMessage":"You must be logged in to do this","messagePattern":"You must be logged in to do this","errorType":"http","errorClass":"HttpError","httpStatus":401,"severity":"error","filePath":"packages/app-store/dub/api/callback.ts","lineNumber":31,"sourceCode":"export default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { code } = req.query;\n\n  const state = decodeOAuthState(req, \"dub\");\n\n  if (typeof code !== \"string\") {\n    if (state?.onErrorReturnTo || state?.returnTo) {\n      res.redirect(\n        getSafeRedirectUrl(state.onErrorReturnTo) ??\n          getSafeRedirectUrl(state?.returnTo) ??\n          `${WEBAPP_URL}/apps/installed`\n      );\n      return;\n    }\n    throw new HttpError({ statusCode: 400, message: \"`code` must be a string\" });\n  }\n\n  if (!req.session?.user?.id) {\n    throw new HttpError({ statusCode: 401, message: \"You must be logged in to do this\" });\n  }\n\n  const { client_id, redirect_uris, client_secret } = await getParsedAppKeysFromSlug(\"dub\", dubAppKeysSchema);\n\n  const codeExchangeUrl = `https://api.dub.co/oauth/token`;\n\n  const result = await fetch(codeExchangeUrl, {\n    method: \"POST\",\n    body: new URLSearchParams({\n      code,\n      client_id,\n      redirect_uri: redirect_uris,\n      client_secret,\n      grant_type: \"authorization_code\",\n    }).toString(),\n    headers: {\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n    },","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/packages/app-store/dub/api/callback.ts#L13-L49","documentation":"Thrown as an HttpError (HTTP 401) by the Dub OAuth callback when the session user has no id. The callback exchanges the Dub authorization code for tokens and must attribute the resulting credential to a Cal.com user; without a session user id, the install cannot be saved.","triggerScenarios":"OAuth callback invoked after the NextAuth session expired mid-flow (user started Dub install, left the page, came back hours later, Dub redirected back), or session cookie was cleared between add and callback.","commonSituations":"Long OAuth round-trip exceeding session TTL; NextAuth cookie blocked; user opened the install in one browser and the callback in another; cookie SameSite=Lax dropping the cookie on the cross-site redirect.","solutions":["Increase session TTL or use a database session strategy so the OAuth round-trip survives.","Set NEXTAUTH cookie SameSite=None; Secure if the Dub redirect is treated as cross-site.","Restart the OAuth flow (re-authenticate) when this fires.","Encode userId in OAuth state so the callback can recover the install context if the session dropped."],"exampleFix":"// before\nif (!req.session?.user?.id) {\n  throw new HttpError({ statusCode: 401, message: \"You must be logged in to do this\" });\n}\n\n// after - redirect to login preserving the install intent\nif (!req.session?.user?.id) {\n  const { code } = req.query;\n  const loginUrl = `${WEBAPP_URL}/auth/login?callbackUrl=${encodeURIComponent(`${req.url}`)}`;\n  res.redirect(loginUrl);\n  return;\n}","handlingStrategy":"validation","validationCode":"const session = await getSession({ req });\nif (!session?.user?.id) {\n  // store the install intent in a cookie/state so it survives re-login\n  res.redirect(`${WEBAPP_URL}/auth/login?callbackUrl=${encodeURIComponent(req.url ?? \"\")}`);\n  return;\n}","typeGuard":"const hasSessionUserId = (req: NextApiRequest): boolean =>\n  typeof req.session?.user?.id === \"number\";","tryCatchPattern":"if (!req.session?.user?.id) {\n  res.redirect(`${WEBAPP_URL}/auth/login?callbackUrl=${encodeURIComponent(req.url ?? \"\")}`);\n  return;\n}","preventionTips":["Use database-backed sessions so the OAuth round-trip survives cookie expiry.","Encode userId in the OAuth state to recover context if the session drops.","Set cookie SameSite appropriately for cross-site OAuth redirects."],"tags":["dub","oauth","callback","authentication","session"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}