Authentication vs. Authorization (They're Not the Same Thing)
"Who Are You" vs. "What Are You Allowed to Do"
Authentication (authn) answers "who is this?" - logging in, verifying a password or token proves you are who you claim to be. Authorization (authz) answers a completely separate question - "is this specific, now-verified person allowed to do this specific thing?" A system can authenticate someone perfectly and still be wrong about what they should be allowed to touch.
// Authentication: verifying WHO this is
func requireAuth(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !validSession(r) {
http.Error(w, "unauthorized", 401)
return
}
next(w, r)
}
}
// Authorization: verifying WHAT they're allowed to do
func requireOwner(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if currentUser(r).ID != resourceOwnerID(r) {
http.Error(w, "forbidden", 403)
return
}
next(w, r)
}
}A real and common vulnerability class - Broken Object Level Authorization - comes directly from confusing these two. An API checks that you're LOGGED IN (authn: 401 if not), but forgets to check that the specific record you're requesting actually BELONGS to you (authz: should be 403 otherwise). The result: a logged-in user can change `/orders/1042` to `/orders/1043` in the URL and see or edit someone else's data, because the endpoint only ever asked "is someone logged in," never "is THIS someone allowed to see THIS thing."
401 vs. 403 exists precisely to distinguish these in a response: 401 means "I don't know who you are" (authenticate first), 403 means "I know exactly who you are, and the answer is no" (an authorization failure). Returning the wrong one is a small thing that makes debugging permission issues meaningfully harder.