{"id":"a9e1611435cdbba1","repo":"gorilla/mux","slug":"forbidden-a9e161","errorCode":null,"errorMessage":"Forbidden","messagePattern":"Forbidden","errorType":"http","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"README.md","lineNumber":571,"sourceCode":"\tamw.tokenUsers[\"00000000\"] = \"user0\"\n\tamw.tokenUsers[\"aaaaaaaa\"] = \"userA\"\n\tamw.tokenUsers[\"05f717e5\"] = \"randomUser\"\n\tamw.tokenUsers[\"deadbeef\"] = \"user0\"\n}\n\n// Middleware function, which will be called for each request\nfunc (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        token := r.Header.Get(\"X-Session-Token\")\n\n        if user, found := amw.tokenUsers[token]; found {\n        \t// We found the token in our map\n        \tlog.Printf(\"Authenticated user %s\\n\", user)\n        \t// Pass down the request to the next middleware (or final handler)\n        \tnext.ServeHTTP(w, r)\n        } else {\n        \t// Write an error and stop the handler chain\n        \thttp.Error(w, \"Forbidden\", http.StatusForbidden)\n        }\n    })\n}\n```\n\n```go\nr := mux.NewRouter()\nr.HandleFunc(\"/\", handler)\n\namw := authenticationMiddleware{tokenUsers: make(map[string]string)}\namw.Populate()\n\nr.Use(amw.Middleware)\n```\n\nNote: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares _should_ write to `ResponseWriter` if they _are_ going to terminate the request, and they _should not_ write to `ResponseWriter` if they _are not_ going to terminate it.\n\n### Handling CORS Requests","sourceCodeStart":553,"sourceCodeEnd":589,"githubUrl":"https://github.com/gorilla/mux/blob/db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265/README.md#L553-L589","documentation":"Not an error thrown by gorilla/mux. README.md:571 is the README copy of the same authentication-middleware example shown in doc.go (error 24): when X-Session-Token isn't found in tokenUsers, the example writes http.Error(w, \"Forbidden\", http.StatusForbidden). Documented separately because it is the more commonly copied source (README > godoc).","triggerScenarios":"A request reaches a handler guarded by the README's authenticationMiddleware without an X-Session-Token header, with an unknown token, or before Populate() has seeded tokenUsers; the middleware short-circuits the chain with 403 \"Forbidden\".","commonSituations":"Front-end not yet sending the auth header; token list out of sync with the client; Populate() skipped during wiring; header name mismatch (e.g. Authorization Bearer vs X-Session-Token); the example pasted as-is into production code paths.","solutions":["Send X-Session-Token set to a token that Populate() registered.","Verify Populate() runs before r.Use(amw.Middleware) and that tokenUsers is populated from your real user store, not the example's hardcoded literals.","Swap the in-memory map for a real authn backend (signed cookie / JWT / database lookup) before exposing the service.","Use 401 for missing credentials and reserve 403 for authenticated-but-unauthorized, so clients can react correctly."],"exampleFix":"// before (README.md:571)\nhttp.Error(w, \"Forbidden\", http.StatusForbidden)\n\n// after\nif token == \"\" {\n    w.Header().Set(\"WWW-Authenticate\", \"Bearer realm=\"\"mux\"\"\")\n    http.Error(w, \"unauthorized\", http.StatusUnauthorized)\n    return\n}\nuser, ok := userStore.BySessionToken(token)\nif !ok {\n    http.Error(w, \"forbidden\", http.StatusForbidden)\n    return\n}","handlingStrategy":"validation","validationCode":"// Validate session token at the boundary; reject empty/malformed before lookup.\nfunc validSessionToken(t string) bool {\n    t = strings.TrimSpace(t)\n    if t == \"\" { return false }\n    // shape check appropriate to your tokens, e.g. opaque >= 16 hex bytes\n    if len(t) < 16 { return false }\n    for _, c := range t {\n        if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {\n            return false\n        }\n    }\n    return true\n}\n\n// in the middleware\ntoken := r.Header.Get(\"X-Session-Token\")\nif !validSessionToken(token) {\n    w.Header().Set(\"WWW-Authenticate\", \"Bearer realm=\\\"mux\\\"\")\n    http.Error(w, \"unauthorized\", http.StatusUnauthorized)\n    return\n}\nuser, ok := store.BySessionToken(token)","typeGuard":null,"tryCatchPattern":"// Treat the example's 403 as a fallback; classify failures explicitly.\nfunc authmw(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        user, status := authenticate(r.Header.Get(\"X-Session-Token\"))\n        if status != 0 {\n            // status is 401 for missing/invalid, 403 for known-but-unauthorized\n            if status == 401 {\n                w.Header().Set(\"WWW-Authenticate\", \"Bearer\")\n            }\n            http.Error(w, http.StatusText(status), status)\n            return\n        }\n        next.ServeHTTP(w, r.WithContext(withUser(r.Context(), user)))\n    })\n}","preventionTips":["Treat the README example as a sketch: replace the hardcoded map with a real session/JWT store before shipping.","Reserve 401 for 'no usable credential' and 403 for 'authenticated but not allowed' so clients can prompt correctly.","Validate token shape and length before any lookup to avoid timing oracles on the map/store.","Write tests asserting both pass-through and rejection paths so a future copy-paste of the example still behaves."],"tags":["authentication","middleware","http","example","authorization","gorilla-mux"],"analyzedSha":"db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265","analyzedAt":"2026-08-04T21:35:47.097Z","schemaVersion":2}