Budibase/budibase · error · Error
Microsoft OAuth callback is missing state
Error message
Microsoft OAuth callback is missing state
What it means
completeSharePointAuth reads `state` from the OAuth callback query string and throws if it is empty. The state parameter is required to correlate the callback with the pending auth request stored in cache and to protect against CSRF.
Source
Thrown at packages/server/src/api/controllers/ai/sharepointAuth.ts:96
authorizeUrl.searchParams.set("response_type", "code")
authorizeUrl.searchParams.set("redirect_uri", callbackUrl)
authorizeUrl.searchParams.set("response_mode", "query")
authorizeUrl.searchParams.set("scope", DEFAULT_SCOPE)
authorizeUrl.searchParams.set("prompt", "select_account")
authorizeUrl.searchParams.set("state", state)
ctx.redirect(authorizeUrl.toString())
}
export async function completeSharePointAuth(ctx: UserCtx<void, void>) {
const authStateCookie = utils.getCookie<DatasourceAuthCookie>(
ctx,
constants.Cookie.DatasourceAuth
)
const state = String(ctx.query.state || "").trim()
if (!state) {
throw new Error("Microsoft OAuth callback is missing state")
}
const statePayload = (await cache.get(
`datasource:${MICROSOFT_PROVIDER}:state:${state}`
)) as { appId?: string; provider?: string }
await cache.destroy(`datasource:${MICROSOFT_PROVIDER}:state:${state}`)
const stateAppId =
typeof statePayload?.appId === "string" ? statePayload.appId.trim() : ""
if (
!statePayload ||
!stateAppId ||
statePayload.provider !== MICROSOFT_PROVIDER
) {
throw new Error("Microsoft OAuth state is invalid or expired")
}
const appId = stateAppId
const oauthError = String(ctx.query.error || "").trim()
if (oauthError) {View on GitHub (pinned to a81a902e9a)
Solutions
- Ensure the redirect_uri registered in Azure AD exactly matches the one used to start the flow, so Microsoft echoes state back.
- Check any reverse proxy config preserves query strings on the callback route.
- Always initiate OAuth via the start endpoint so a state value is generated and stored in cache.
Example fix
// before
callbackUrl = `${base}/api/ai/sharepoint/callback` // query stripped by redirect
// after
callbackUrl = `${base}/api/ai/sharepoint/callback?state=${encodeURIComponent(state)}` Defensive patterns
Strategy: try-catch
Validate before calling
const url = new URL(callbackUrl)
if (!url.searchParams.get('state')) throw new Error('callback URL must include state query param') Try / catch
try {
await completeSharePointAuth(ctx)
} catch (e) {
if (e.message === 'Microsoft OAuth callback is missing state') {
// redirect user to restart the OAuth flow
} else throw e
} Prevention
- Always start OAuth via the official start endpoint so state is generated.
- Verify proxies/reverse-proxies preserve query strings on the callback route.
- Test the full browser redirect flow, not just the callback endpoint in isolation.
When it happens
Trigger: Microsoft redirects to the callback URL without ?state=..., or the client hits the callback endpoint directly/manually with a stripped-down URL.
Common situations: Redirect URI misconfigured so query params are dropped; a proxy or reverse proxy rewrites the callback URL and strips the query string; user bookmarked a partial callback URL; manual testing of the callback endpoint.
Related errors
- Microsoft OAuth callback is missing the authorization code
- No Microsoft datasource configuration found
- Microsoft OAuth state is invalid or expired
- Microsoft OAuth authorization failed
- Failed to exchange Microsoft OAuth code
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/ccadb3a0f36ed434.
Report an issue: GitHub.