The Nightmare Named “Spaghetti Authorization”
My first Go project almost collapsed because of a mess of authorization if-else statements. Initially, everything was simple: just check if a user is an admin to allow post editing. However, when the system hit 10,000 users, clients started making “tough” demands.
They wanted to add Editor, Moderator, and even Guest roles. The trickiest part was conditions like: “Editors can only edit their own posts” or “Moderators can only delete comments from 8 AM to 5 PM.” Within just two weeks, my code was drowning in permission logic. Every time a small rule changed, I had to dig through dozens of files, modify the code, and rebuild the entire application. That is the “Hardcoded Authorization” trap many developers fall into.
Why Custom Authorization Code Often Fails to Scale
After spending a whole week refactoring, I identified three reasons why custom-built systems quickly become overwhelmed:
- Tight Coupling: Authorization logic is scattered throughout the business logic. If you want to change the role structure, you have to modify code everywhere.
- Difficulty in Implementing ABAC: RBAC (Role-Based Access Control) is easy. But when you encounter ABAC (Attribute-Based Access Control—for example, only the post owner has edit rights), the
if-elseblocks turn into a literal maze. - Security Risks: Every team member might check permissions in a slightly different way. One moment of carelessness can leave a vulnerability for hackers to exploit.
Searching for the Optimal Solution
I considered three options before finding “the one”:
- Building Custom DB Tables: Creating
rolesandpermissionstables. This approach is high-maintenance and extremely difficult when handling Role Hierarchy. - Simple Middleware: This can only block at the router level and doesn’t solve fine-grained authorization for specific resources.
- Using Casbin: A specialized library that supports the PERM model (Policy, Effect, Request, Matchers). This is the most professional approach.
Casbin – Decoupling the Rules from the Data
After running Casbin in production for six months on an e-commerce platform with over 50 API endpoints, I know it was the right decision. The biggest advantage of Casbin is that it separates the Mechanism from the Policy.
The PERM Model: The Backbone of Casbin
You only need to define the model.conf file once. It dictates how the system operates through four components:
- Request (r): Who (sub) is doing what (act) on which resource (obj)?
- Policy (p): A list of allowed rules.
- Matchers (m): The formula to match the Request with the Policy.
- Effect (e): The final decision (e.g., just one matching rule is enough to allow).
Practical Implementation with Go
Installing the library is extremely simple:
go get github.com/casbin/casbin/v2
Below is a model.conf file for an RBAC system that supports role inheritance:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
Applying it in Go code to check access permissions:
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
)
func main() {
e, _ := casbin.NewEnforcer("model.conf", "policy.csv")
sub := "alice" // User
obj := "data1" // Resource
act := "read" // Action
ok, err := e.Enforce(sub, obj, act)
if err != nil {
fmt.Println("System error")
} else if ok {
fmt.Println("Access granted!")
} else {
fmt.Println("You do not have permission!")
}
}
When working with complex policy structures, I often use toolcraft.app to format JSON or quickly test data. It helps me avoid installing too many extensions that bloat VS Code.
Upgrading to ABAC: Real Power
If you want to specify: “Only the post owner can edit,” you don’t need to modify the Go code. Just update the Matcher in the configuration file:
[matchers]
m = r.sub == r.obj.Owner || r.sub == "admin"
This level of customization allows you to handle even the most unusual logic requested by clients without cluttering your business code.
Hard-Earned Lessons When Using Casbin
After half a year in the trenches, I’ve gathered some practical experience:
- Say No to CSV in Production: Use a Gorm Adapter to store policies in PostgreSQL or MySQL. CSV files are only suitable for quick testing or demos.
- Caching is Vital: Permission checks happen on every request. When policies reach thousands of lines, use Redis to cache the results. Response time should be under 1ms.
- Don’t Skimp on Comments: Thoroughly annotate your
model.conffile. Trust me, three months later, you won’t understand those Matchers without comments.
Conclusion
Casbin isn’t the solution for every problem, but it is the most powerful tool for handling authorization in Go. It completely decouples security logic, helping dev and ops teams understand each other better without reading code. If your project is getting complex, try Casbin today.

