{"record":{"id":"28963b91fe41cc4f","repo":"vxcontrol/pentagi","slug":"auth-invalidauthorizationstate","errorCode":"Auth.InvalidAuthorizationState","errorMessage":"state parameter is required","messagePattern":"state parameter is required","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"backend/pkg/server/services/auth.go","lineNumber":357,"sourceCode":"// @Router /auth/login-callback [get]\nfunc (s *AuthService) AuthLoginGetCallback(c *gin.Context) {\n\tcode := c.Query(\"code\")\n\tif code == \"\" {\n\t\tresponse.Error(c, response.ErrAuthInvalidLoginCallbackRequest, fmt.Errorf(\"code is required\"))\n\t\treturn\n\t}\n\n\tstate, err := c.Request.Cookie(s.stateCookieName())\n\tif err != nil {\n\t\tlogger.FromContext(c).WithError(err).Errorf(\"error getting state from cookie\")\n\t\tresponse.Error(c, response.ErrAuthInvalidAuthorizationState, err)\n\t\treturn\n\t}\n\n\tqueryState := c.Query(\"state\")\n\tif queryState == \"\" {\n\t\tlogger.FromContext(c).Errorf(\"error missing state parameter in OAuth callback\")\n\t\tresponse.Error(c, response.ErrAuthInvalidAuthorizationState, fmt.Errorf(\"state parameter is required\"))\n\t\treturn\n\t}\n\n\tif queryState != state.Value {\n\t\tlogger.FromContext(c).Errorf(\"error matching received state to stored one\")\n\t\tresponse.Error(c, response.ErrAuthInvalidAuthorizationState, nil)\n\t\treturn\n\t}\n\n\tstateData, err := s.parseState(c, state.Value)\n\tif err != nil {\n\t\treturn\n\t}\n\n\ts.authLoginCallback(c, stateData, code)\n}\n\n// AuthLoginPostCallback is function to catch login callback from OAuth application","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/auth.go#L339-L375","documentation":"Auth.InvalidAuthorizationState is returned when the OAuth callback is missing the 'state' query parameter. The state parameter is the CSRF protection token that must match the HMAC-signed value stored in the state cookie; without it the server cannot correlate the callback to the original login attempt. It is also returned when the state does not match the cookie value (errors logged, nil error passed).","triggerScenarios":"GET /auth/login-callback without ?state=...; or the state value differs from the one stored in the state cookie (same error code, this specific message only when the query param is empty).","commonSituations":"Browser blocking/stripping cookies (third-party cookie restrictions) so the state cookie is missing; IdP configured to drop the state parameter; user opening the callback link in a different browser than the one that started login; SPA stripping query params before calling the backend.","solutions":["Make sure the browser sends the state cookie (same-site settings, same domain, cookies not blocked)","Start the login flow again via /auth/login so a fresh state parameter and cookie are issued","Verify the IdP passes the state parameter through unchanged on redirect","Check proxies/CDNs are not stripping query parameters from the callback URL"],"exampleFix":"// before\nconst cb = '/auth/login-callback'\n// after\nconst params = new URLSearchParams(redirectParams)\nconst cb = `/auth/login-callback?code=${params.get('code')}&state=${params.get('state')}`","handlingStrategy":"validation","validationCode":"const url = new URL(window.location.href)\nif (!url.searchParams.get('state')) {\n  // cookies likely blocked or flow broken; restart login\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Allow cookies for the auth domain (same-site policy)","Never strip query params before forwarding to the backend","Start and finish login in the same browser session","Confirm the IdP echoes the state parameter"],"tags":["oauth2","csrf","state-mismatch"],"backgroundTag":"oauth2-state-mismatch","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}