{"record":{"id":"bd46dfd91d0d5bfe","repo":"multica-ai/multica","slug":"invalid-claims","errorCode":null,"errorMessage":"invalid claims","messagePattern":"invalid claims","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"server/internal/middleware/auth.go","lineNumber":218,"sourceCode":"\t\t\t}\n\n\t\t\t// JWT\n\t\t\ttoken, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {\n\t\t\t\tif _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {\n\t\t\t\t\treturn nil, jwt.ErrSignatureInvalid\n\t\t\t\t}\n\t\t\t\treturn auth.JWTSecret(), nil\n\t\t\t})\n\t\t\tif err != nil || !token.Valid {\n\t\t\t\tslog.Warn(\"auth: invalid token\", \"path\", r.URL.Path, \"error\", err)\n\t\t\t\thttp.Error(w, `{\"error\":\"invalid token\"}`, http.StatusUnauthorized)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tclaims, ok := token.Claims.(jwt.MapClaims)\n\t\t\tif !ok {\n\t\t\t\tslog.Warn(\"auth: invalid claims\", \"path\", r.URL.Path)\n\t\t\t\thttp.Error(w, `{\"error\":\"invalid claims\"}`, http.StatusUnauthorized)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tsub, ok := claims[\"sub\"].(string)\n\t\t\tif !ok || strings.TrimSpace(sub) == \"\" {\n\t\t\t\tslog.Warn(\"auth: invalid claims\", \"path\", r.URL.Path)\n\t\t\t\thttp.Error(w, `{\"error\":\"invalid claims\"}`, http.StatusUnauthorized)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tr.Header.Set(\"X-User-ID\", sub)\n\t\t\tif email, ok := claims[\"email\"].(string); ok {\n\t\t\t\tr.Header.Set(\"X-User-Email\", email)\n\t\t\t}\n\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/middleware/auth.go#L200-L236","documentation":"HTTP 401 returned when the parsed JWT's claims cannot be asserted as jwt.MapClaims. With golang-jwt this is nearly unreachable for tokens parsed with the default parser (MapClaims is the default claims type); hitting it means the parser was configured with a custom claims factory or the token is structurally degenerate. It signals the claim-extraction contract is broken, not that values inside the claims are wrong.","triggerScenarios":"Token parsed with a Parser configured to use a custom ClaimFactory returning a non-MapClaims type, or an edge-case token that yields nil claims; then this middleware's MapClaims assertion fails.","commonSituations":"Library version change altering parser defaults; a shared parsing helper configured elsewhere with custom claims; malformed token crafted to have an empty payload.","solutions":["Issue tokens through the standard login flow so claims are standard JSON objects.","If you control parsing, use the default parser (MapClaims) upstream of this middleware.","Regenerate the token — a structurally valid login-issued JWT will not hit this branch.","Check for library version drift in go.mod if this appears after an upgrade."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func hasMapClaims(tok string) bool {\n    t, _, err := jwt.NewParser().ParseUnverified(tok, jwt.MapClaims{})\n    return err == nil && t != nil\n}","tryCatchPattern":"resp, err := client.Do(req)\nif err == nil && resp.StatusCode == 401 {\n    body, _ := io.ReadAll(resp.Body)\n    if strings.Contains(string(body), \"invalid claims\") { tok = relogin() } // token shape wrong, not secret wrong\n}","preventionTips":["Mint JWTs only via the platform's login endpoint.","Use the default golang-jwt parser upstream (MapClaims).","Pin library versions to avoid parser default drift."],"tags":["authentication","jwt","http-401","claims"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}