{"record":{"id":"1ba5770c1d551c31","repo":"hashicorp/nomad","slug":"missing-state","errorCode":null,"errorMessage":"missing state","messagePattern":"missing state","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":2387,"sourceCode":"\tRedirectURI string\n\n\tWriteRequest\n}\n\n// Validate ensures the request object contains all the required fields in\n// order to complete the OIDC authentication flow.\nfunc (a *ACLOIDCCompleteAuthRequest) Validate() error {\n\n\tvar mErr multierror.Error\n\n\tif a.AuthMethodName == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"missing auth method name\"))\n\t}\n\tif a.ClientNonce == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"missing client nonce\"))\n\t}\n\tif a.State == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"missing state\"))\n\t}\n\tif a.Code == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"missing code\"))\n\t}\n\tif a.RedirectURI == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"missing redirect URI\"))\n\t}\n\treturn mErr.ErrorOrNil()\n}\n\n// ACLLoginResponse is the response when the auth flow has been\n// completed successfully.\ntype ACLLoginResponse struct {\n\tACLToken *ACLToken\n\tWriteMeta\n}\n\n// ACLLoginRequest is the request object to begin auth with an external","sourceCodeStart":2369,"sourceCodeEnd":2405,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L2369-L2405","documentation":"ACLOIDCCompleteAuthRequest.Validate requires State to be non-empty. The opaque state value issued by the OIDC provider (and tracked by Nomad) protects against CSRF and links the callback to the original request; an empty State yields 'missing state'.","triggerScenarios":"Calling the OIDC complete-auth endpoint with ACLOIDCCompleteAuthRequest.State == \"\", e.g. the user lands on the callback without a state query parameter or the handler drops it when building the request.","commonSituations":"IdP redirects to the callback without state (misconfigured provider); callback handler only reads 'code'; user manually pasting a truncated callback URL; state lost in cross-domain redirects.","solutions":["Read the state query parameter from the provider callback and set State on the request","Fix the OIDC provider client config so state is included in the redirect","Verify the full callback URL (including state) reaches your handler"],"exampleFix":"// before\nreq := &structs.ACLOIDCCompleteAuthRequest{AuthMethodName: m, ClientNonce: n, Code: r.URL.Query().Get(\"code\")}\n// after\nreq := &structs.ACLOIDCCompleteAuthRequest{AuthMethodName: m, ClientNonce: n, State: r.URL.Query().Get(\"state\"), Code: r.URL.Query().Get(\"code\")}","handlingStrategy":"validation","validationCode":"if req.State == \"\" { return errors.New(\"State from the provider callback is required\") }","typeGuard":"func hasState(req *structs.ACLOIDCCompleteAuthRequest) bool { return req != nil && req.State != \"\" }","tryCatchPattern":"if err := req.Validate(); err != nil {\n  if strings.Contains(err.Error(), \"missing state\") { /* treat as CSRF-suspect callback; abort login */ }\n}","preventionTips":["Always read and forward the state query parameter from the callback","Configure the IdP to include state in redirects","Reject callbacks lacking state before calling the API"],"tags":["nomad","acl","oidc","validation","csrf-state"],"backgroundTag":"missing-oauth-state","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}