{"record":{"id":"9fd186d73ac8fb52","repo":"Budibase/budibase","slug":"microsoft-oauth-state-is-invalid-or-expired","errorCode":null,"errorMessage":"Microsoft OAuth state is invalid or expired","messagePattern":"Microsoft OAuth state is invalid or expired","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/api/controllers/ai/sharepointAuth.ts","lineNumber":109,"sourceCode":"    constants.Cookie.DatasourceAuth\n  )\n\n  const state = String(ctx.query.state || \"\").trim()\n  if (!state) {\n    throw new Error(\"Microsoft OAuth callback is missing state\")\n  }\n  const statePayload = (await cache.get(\n    `datasource:${MICROSOFT_PROVIDER}:state:${state}`\n  )) as { appId?: string; provider?: string }\n  await cache.destroy(`datasource:${MICROSOFT_PROVIDER}:state:${state}`)\n  const stateAppId =\n    typeof statePayload?.appId === \"string\" ? statePayload.appId.trim() : \"\"\n  if (\n    !statePayload ||\n    !stateAppId ||\n    statePayload.provider !== MICROSOFT_PROVIDER\n  ) {\n    throw new Error(\"Microsoft OAuth state is invalid or expired\")\n  }\n  const appId = stateAppId\n\n  const oauthError = String(ctx.query.error || \"\").trim()\n  if (oauthError) {\n    const description = String(ctx.query.error_description || \"\").trim()\n    console.error(\"Microsoft OAuth authorization failed\", {\n      appId,\n      error: oauthError,\n      hasDescription: !!description,\n    })\n    throw new Error(\"Microsoft OAuth authorization failed\")\n  }\n\n  const code = String(ctx.query.code || \"\").trim()\n  if (!code) {\n    throw new Error(\n      \"Microsoft OAuth callback is missing the authorization code\"","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/sharepointAuth.ts#L91-L127","documentation":"After reading the state param, completeSharePointAuth looks it up in the cache and validates that a payload exists, its appId is a non-empty string, and its provider equals MICROSOFT_PROVIDER. Any mismatch or missing/expired cache entry throws this Error. Note the state cache entry is destroyed on read, so it is single-use.","triggerScenarios":"Callback arrives more than the state TTL after flow start; the same callback URL is replayed a second time (state already consumed/destroyed); cache (Redis) restarted or flushed between start and callback; multi-node deployment where callback hits a node without the cached state; state was forged with an unknown provider value.","commonSituations":"Users double-clicking authorize links or refreshing the callback page; short-lived cache entries with slow manual auth; Redis eviction under memory pressure; mismatched provider constant after config changes.","solutions":["Restart the OAuth flow from the beginning to generate a fresh state.","Check cache (Redis) availability, TTL for the state key, and that all server nodes share the same cache instance.","Avoid replaying callback URLs; the state is destroyed after first successful validation.","Confirm the provider stored at state-creation time matches the Microsoft provider constant used at callback."],"exampleFix":"// before\n// replaying an already-consumed callback URL (state destroyed on first read)\n// after\nawait startSharePointAuth(appId) // generate fresh state, then complete flow once","handlingStrategy":"retry","validationCode":"// no caller-side check possible; ensure flow is completed promptly and only once after start","typeGuard":null,"tryCatchPattern":"try {\n  await completeSharePointAuth(ctx)\n} catch (e) {\n  if (e.message === 'Microsoft OAuth state is invalid or expired') {\n    // do not retry the same callback; restart the whole OAuth flow\n    return restartSharePointAuth(appId)\n  } else throw e\n}","preventionTips":["Never replay or refresh OAuth callback URLs — state is single-use.","Complete the flow promptly before the state cache TTL expires.","Use a shared, persistent cache (Redis) across all server instances."],"tags":["oauth","cache","session-expired","microsoft","sharepoint"],"backgroundTag":"oauth-state-missing-or-invalid","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}