{"record":{"id":"6e4290648ed0e98c","repo":"go-kit/kit","slug":"unauthorized-access","errorCode":null,"errorMessage":"Unauthorized Access","messagePattern":"Unauthorized Access","errorType":"exception","errorClass":"ErrUnauthorized","httpStatus":403,"severity":"error","filePath":"auth/casbin/middleware.go","lineNumber":40,"sourceCode":"\tCasbinPolicyContextKey contextKey = \"CasbinPolicy\"\n\n\t// CasbinEnforcerContextKey holds the key to retrieve the active casbin\n\t// Enforcer.\n\tCasbinEnforcerContextKey contextKey = \"CasbinEnforcer\"\n)\n\nvar (\n\t// ErrModelContextMissing denotes a casbin model was not passed into\n\t// the parsing of middleware's context.\n\tErrModelContextMissing = errors.New(\"CasbinModel is required in context\")\n\n\t// ErrPolicyContextMissing denotes a casbin policy was not passed into\n\t// the parsing of middleware's context.\n\tErrPolicyContextMissing = errors.New(\"CasbinPolicy is required in context\")\n\n\t// ErrUnauthorized denotes the subject is not authorized to do the action\n\t// intended on the given object, based on the context model and policy.\n\tErrUnauthorized = errors.New(\"Unauthorized Access\")\n)\n\n// NewEnforcer checks whether the subject is authorized to do the specified\n// action on the given object. If a valid access control model and policy\n// is given, then the generated casbin Enforcer is stored in the context\n// with CasbinEnforcer as the key.\nfunc NewEnforcer(\n\tsubject string, object interface{}, action string,\n) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\tcasbinModel := ctx.Value(CasbinModelContextKey)\n\t\t\tcasbinPolicy := ctx.Value(CasbinPolicyContextKey)\n\t\t\tenforcer, err := stdcasbin.NewEnforcer(casbinModel, casbinPolicy)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/go-kit/kit/blob/78fbbceece7bbcf073bee814a7772f4397ea756c/auth/casbin/middleware.go#L22-L58","documentation":"Returned by go-kit's casbin.NewEnforcer middleware when enforcer.Enforce(subject, object, action) completed without error but returned false. Unlike the context errors, this is the policy itself deciding the subject may not perform the action on the object under the configured model. It is the expected denial path, not a malfunction.","triggerScenarios":"The policy file/adapter has no rule matching the (subject, object, action) triple, e.g. policy.csv lacks 'p, alice, data1, read'; the subject string doesn't match what's in the policy (hardcoded placeholder instead of the JWT claim subject); object/action typos; RBAC role not assigned ('g' grouping rules missing); policy_effect definition in model.conf evaluates to deny.","commonSituations":"New endpoint protected before policy rules are written; subject taken from a different identity field than the one used when issuing rules; role assignments lost after policy reload; ABAC model where attribute matching silently fails; environment whose policy file is an old version.","solutions":["Add the missing rule to the policy: p, <subject>, <object>, <action> (and the g grouping rule for RBAC roles)","Verify the subject passed to NewEnforcer matches the identity in the policy — derive it from the JWT claims stored under jwt.JWTClaimsContextKey, not a hardcoded string","Test the triple directly with a standalone casbin.Enforcer (enforcer.Enforce(sub, obj, act)) to confirm the model+policy behave as expected","Check the policy_effect section of model.conf if rules exist but everything is still denied"],"exampleFix":"// before: hardcoded subject matches no policy rule\ne := casbin.NewEnforcer(\"user\", \"data1\", \"read\")(myEndpoint)\n// policy.csv: p, alice, data1, read  -> everyone else denied\n\n// after: use the authenticated subject from JWT claims\nvar e endpoint.Endpoint = endpoint.Chain(\n\tfunc(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\t\tclaims := ctx.Value(jwt.JWTClaimsContextKey).(jwt.MapClaims)\n\t\t\tsub, _ := claims[\"sub\"].(string)\n\t\t\treturn next(context.WithValue(ctx, subKey, sub), req)\n\t\t}\n\t},\n)(myEndpoint)\n// and ensure policy.csv contains: p, <that-sub>, data1, read","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"resp, err := ep(ctx, req)\nif err != nil {\n\tif errors.Is(err, casbin.ErrUnauthorized) {\n\t\t// map to 403 Forbidden; do NOT retry — the policy denied it\n\t\treturn nil, httptransport.ErrorOnlyEncoder // or your error encoder returning 403\n\t}\n\treturn nil, err\n}","preventionTips":["Derive the casbin subject from the authenticated JWT claims rather than client input so policy rules and identities stay in sync","Maintain policy rules next to endpoint definitions in code review; a protected endpoint without a matching rule ships a guaranteed 403","Add a unit test that enforces a representative (subject, object, action) triple for each protected endpoint"],"tags":["go","go-kit","casbin","authorization","rbac","access-denied"],"backgroundTag":null,"analyzedSha":"78fbbceece7bbcf073bee814a7772f4397ea756c","analyzedAt":"2026-08-15T22:31:35.570Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}