{"id":"1c0a98780ad1bda2","repo":"gorilla/mux","slug":"forbidden","errorCode":null,"errorMessage":"Forbidden","messagePattern":"Forbidden","errorType":"http","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"doc.go","lineNumber":290,"sourceCode":"\t// Initialize it somewhere\n\tfunc (amw *authenticationMiddleware) Populate() {\n\t\tamw.tokenUsers[\"00000000\"] = \"user0\"\n\t\tamw.tokenUsers[\"aaaaaaaa\"] = \"userA\"\n\t\tamw.tokenUsers[\"05f717e5\"] = \"randomUser\"\n\t\tamw.tokenUsers[\"deadbeef\"] = \"user0\"\n\t}\n\n\t// Middleware function, which will be called for each request\n\tfunc (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\ttoken := r.Header.Get(\"X-Session-Token\")\n\n\t\t\tif user, found := amw.tokenUsers[token]; found {\n\t\t\t\t// We found the token in our map\n\t\t\t\tlog.Printf(\"Authenticated user %s\\n\", user)\n\t\t\t\tnext.ServeHTTP(w, r)\n\t\t\t} else {\n\t\t\t\thttp.Error(w, \"Forbidden\", http.StatusForbidden)\n\t\t\t}\n\t\t})\n\t}\n\n\tr := mux.NewRouter()\n\tr.HandleFunc(\"/\", handler)\n\n\tamw := authenticationMiddleware{tokenUsers: make(map[string]string)}\n\tamw.Populate()\n\n\tr.Use(amw.Middleware)\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.\n*/\npackage mux\n","sourceCodeStart":272,"sourceCodeEnd":306,"githubUrl":"https://github.com/gorilla/mux/blob/db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265/doc.go#L272-L306","documentation":"This is NOT an error thrown by gorilla/mux. It is a line in the package doc.go authentication-middleware example (doc.go:290): when the X-Session-Token header is absent or not present in the in-memory tokenUsers map, the example calls http.Error(w, \"Forbidden\", http.StatusForbidden). It is a pattern a developer copy-pastes; the 'error' is the 403 the example emits.","triggerScenarios":"A request hits a handler guarded by the example authenticationMiddleware with no X-Session-Token header, an empty token, or a token not added via Populate(); the middleware returns 403 with body \"Forbidden\".","commonSituations":"Client omitted the auth header; token rotated/expired and was never updated in the map; Populate() wasn't called before the router started so the map is empty; header name casing or trailing whitespace differs from \"X-Session-Token\"; the example was pasted verbatim into production.","solutions":["Send X-Session-Token with one of the values seeded by Populate() (e.g. 00000000).","Ensure amw.Populate() runs (and is seeded with real tokens) before r.Use(amw.Middleware) wires the route.","Replace the toy map with a real auth mechanism (signed session cookie, JWT, or a lookup against your user store).","Return 401 Unauthorized (and a WWW-Authenticate header) instead of 403 when no credential was supplied, reserving 403 for valid-but-insufficient credentials."],"exampleFix":"// before (doc.go example, verbatim)\nhttp.Error(w, \"Forbidden\", http.StatusForbidden)\n\n// after: distinguish missing (401) from invalid (403), don't ship the toy map\nif token == \"\" {\n    w.Header().Set(\"WWW-Authenticate\", \"Bearer\")\n    http.Error(w, \"unauthorized\", http.StatusUnauthorized)\n    return\n}\nuser, ok := store.Lookup(token)\nif !ok {\n    http.Error(w, \"forbidden\", http.StatusForbidden)\n    return\n}","handlingStrategy":"validation","validationCode":"// Validate the token at the trust boundary before invoking next.ServeHTTP.\nfunc lookupToken(store TokenStore, token string) (user string, ok bool) {\n    if len(token) < minTokenLen { // reject obviously bogus tokens early\n        return \"\", false\n    }\n    return store.Lookup(token)\n}\n\n// in the middleware\ntoken := strings.TrimSpace(r.Header.Get(\"X-Session-Token\"))\nuser, ok := lookupToken(store, token)\nif !ok {\n    respondUnauthorized(w) // 401 with WWW-Authenticate, not the toy 403\n    return\n}","typeGuard":null,"tryCatchPattern":"// Wrap the example middleware so failures are typed, not bare 403 strings.\ntype AuthError struct{ Reason string; Status int }\nfunc (e *AuthError) Error() string { return e.Reason }\n\nfunc authMiddleware(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        user, err := authenticate(r) // returns *AuthError on failure\n        if err != nil {\n            ae := err.(*AuthError)\n            http.Error(w, ae.Reason, ae.Status)\n            return\n        }\n        next.ServeHTTP(w, r.WithContext(withUser(r.Context(), user)))\n    })\n}","preventionTips":["Don't ship the example's hardcoded tokenUsers map — wire it to a real credential store.","Distinguish 401 (no/invalid credential) from 403 (authenticated but unauthorized) so clients can react correctly.","Constant-time compare tokens and rotate them on a schedule; revoke via store deletion rather than editing source.","Run an integration test that asserts both 'valid token passes' and 'missing/unknown token yields 401/403'."],"tags":["authentication","middleware","http","example","authorization","gorilla-mux"],"analyzedSha":"db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265","analyzedAt":"2026-08-04T21:35:47.097Z","schemaVersion":2}