{"record":{"id":"7f8e33ca83e69e62","repo":"Budibase/budibase","slug":"unable-to-fetch-datasource-auth-cookie","errorCode":null,"errorMessage":"Unable to fetch datasource auth cookie","messagePattern":"Unable to fetch datasource auth cookie","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/backend-core/src/middleware/passport/datasource/google.ts","lineNumber":67,"sourceCode":"}\n\nexport async function postAuth(\n  passport: Passport,\n  ctx: UserCtx,\n  next: Function\n) {\n  // get the relevant config\n  const config = await fetchGoogleCreds()\n  const platformUrl = await configs.getPlatformUrl({ tenantAware: false })\n\n  let callbackUrl = `${platformUrl}/api/global/auth/datasource/google/callback`\n  const authStateCookie = utils.getCookie<{ appId: string }>(\n    ctx,\n    Cookie.DatasourceAuth\n  )\n\n  if (!authStateCookie) {\n    throw new Error(\"Unable to fetch datasource auth cookie\")\n  }\n\n  return passport.authenticate(\n    new GoogleStrategy(\n      {\n        clientID: config.clientID,\n        clientSecret: config.clientSecret,\n        callbackURL: callbackUrl,\n      },\n      (\n        accessToken: string,\n        refreshToken: string,\n        _profile: SSOProfile,\n        done: Function\n      ) => {\n        utils.clearCookie(ctx, Cookie.DatasourceAuth)\n        done(null, { accessToken, refreshToken })\n      }","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/backend-core/src/middleware/passport/datasource/google.ts#L49-L85","documentation":"postAuth in the Google datasource passport flow reads the DatasourceAuth state cookie that preAuth set, and throws when the cookie is absent. The cookie carries the appId/state needed to complete OAuth, so without it the callback cannot proceed.","triggerScenarios":"Hitting the OAuth callback without having gone through preAuth, cookies blocked/stripped by the browser or proxy, cookie expiring, or the callback occurring on a different domain than the preAuth request.","commonSituations":"Third-party cookie blocking in browsers (Safari); opening the callback URL directly; SameSite/proxy config stripping Set-Cookie; session timeout between authorize and callback steps.","solutions":["Ensure the flow always begins at the preAuth endpoint so the DatasourceAuth cookie is set","Enable cookies for the builder/proxy domain (check SameSite/Secure settings and HTTPS)","Verify nginx/proxy is not stripping cookies on the callback route","Complete the OAuth flow promptly — restart from preAuth if the session expired","Make sure preAuth and callback share the same host/domain"],"exampleFix":"// before\n// calling postAuth callback directly without preAuth\nawait google.postAuth(ctx, passport)\n// after\n// route the user through preAuth first: GET /datasources/google/auth -> callback\nrouter.get(\"/datasources/google/auth\", async ctx => {\n  await google.preAuth(passport, ctx)\n})","handlingStrategy":"fallback","validationCode":"const state = utils.getCookie(ctx, Cookie.DatasourceAuth)\nif (!state) throw new Error(\"OAuth state cookie missing — restart flow from preAuth\")","typeGuard":null,"tryCatchPattern":"try {\n  await google.postAuth(ctx, passport)\n} catch (e) {\n  if (e.message === \"Unable to fetch datasource auth cookie\") {\n    return ctx.redirect(googleDatasourceAuthUrl) // restart OAuth from preAuth\n  }\n  throw e\n}","preventionTips":["Always start OAuth at preAuth, never at the callback URL directly","Configure SameSite=None; Secure cookies behind HTTPS for cross-domain callbacks","Ensure proxies don't strip Set-Cookie on callback routes","Shorten the gap between auth start and callback to avoid expiry"],"tags":["google","oauth","cookie","state"],"backgroundTag":"missing-oauth-state-cookie","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}