{"record":{"id":"1bb719f04cba9106","repo":"crowdsecurity/crowdsec","slug":"missing-scenarios-list-in-login-request-for-tls-au","errorCode":null,"errorMessage":"missing scenarios list in login request for TLS auth: %w","messagePattern":"missing scenarios list in login request for TLS auth: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/apiserver/middlewares/v1/jwt.go","lineNumber":117,"sourceCode":"\t} else if err != nil {\n\t\treturn nil, fmt.Errorf(\"while selecting machine entry for %s: %w\", ret.machineID, err)\n\t} else {\n\t\tif ret.clientMachine.AuthType != types.TlsAuthType {\n\t\t\treturn nil, fmt.Errorf(\"machine %s attempted to auth with TLS cert but it is configured to use %s\", ret.machineID, ret.clientMachine.AuthType)\n\t\t}\n\n\t\tret.machineID = ret.clientMachine.MachineId\n\t}\n\n\tloginInput := struct {\n\t\tScenarios []string `json:\"scenarios\"`\n\t}{\n\t\tScenarios: []string{},\n\t}\n\n\terr = c.ShouldBindJSON(&loginInput)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"missing scenarios list in login request for TLS auth: %w\", err)\n\t}\n\n\tret.scenariosInput = loginInput.Scenarios\n\n\treturn &ret, nil\n}\n\nfunc (j *JWT) authPlain(c *gin.Context) (*authInput, error) {\n\tvar (\n\t\tloginInput models.WatcherAuthRequest\n\t\terr        error\n\t)\n\n\tctx := c.Request.Context()\n\n\tret := authInput{}\n\n\tif err = c.ShouldBindJSON(&loginInput); err != nil {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiserver/middlewares/v1/jwt.go#L99-L135","documentation":"In TLS auth the login request body must contain a 'scenarios' list (used for license/usage tracking even though certs do the real auth). ShouldBindJSON failed — body empty, malformed JSON, or wrong content-type — so authTLS wraps it as 'missing scenarios list in login request for TLS auth'.","triggerScenarios":"Authenticator -> authTLS: a mTLS client POSTs /api/v1/watchers/login without a JSON body, with invalid JSON, or without Content-Type: application/json; c.ShouldBindJSON returns a binding error which is wrapped.","commonSituations":"Hand-rolled scripts curling the login endpoint with no body; old/patched agent versions that omit the scenarios field; proxy stripping request bodies; testing with 'curl -X POST' and no -d.","solutions":["Send a valid JSON body, e.g. {\"scenarios\":[]}, with Content-Type: application/json when logging in via TLS","Upgrade the crowdsec agent/bouncer to a version that sends scenarios on TLS login","Test with: curl --cert ... --key ... -H 'Content-Type: application/json' -d '{\"scenarios\":[]}' https://lapi:8080/api/v1/watchers/login','If you control a custom client, always include the scenarios key (empty list is acceptable)"],"exampleFix":"// before\ncurl -sk --cert client.pem --key client.key -X POST https://lapi:8080/api/v1/watchers/login\n// after\ncurl -sk --cert client.pem --key client.key -H 'Content-Type: application/json' -d '{\"scenarios\":[]}' -X POST https://lapi:8080/api/v1/watchers/login","handlingStrategy":"validation","validationCode":"// validate the TLS login payload before sending\nbody := map[string][]string{\"scenarios\": {}}\ndata, _ := json.Marshal(body)\nif !json.Valid(data) {\n    return errors.New(\"invalid TLS login body\")\n}\nreq.Header.Set(\"Content-Type\", \"application/json\")","typeGuard":null,"tryCatchPattern":"resp, err := http.Do(tlsLoginReq)\nif err != nil || (resp != nil && resp.StatusCode == http.StatusBadRequest) {\n    return fmt.Errorf(\"TLS login rejected, ensure body is {\\\"scenarios\\\":[]} with JSON content-type: %w\", err)\n}","preventionTips":["Always send {\"scenarios\":[]} (or a real list) with Content-Type: application/json on TLS login","Use supported crowdsec client versions that include scenarios in TLS login","When scripting, never use bare -X POST without a JSON body","Test custom integrations against a local LAPI before production"],"tags":["tls","auth","http","request-binding","go"],"backgroundTag":"schema-validation-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}