Authentication answers 'who are you?' Authorization answers 'what are you allowed to do?' Authentication always comes first — you can't decide someone's permissions until you know who they are.
The building analogy: authentication is showing your ID at the entrance to prove you're an employee. Authorization is whether your badge opens the server room. Everyone who gets in is authenticated; only some are authorized for that door.
In a web app, concretely:
- **Authentication** is login. Email and password, a magic link, an OTP, Google sign-in, biometrics. Its output is a session or token proving identity for subsequent requests.
- **Authorization** is every check after that. Can this user edit *this* post? Can they view the admin dashboard? Can they delete another user's comment? Its output is allow or deny for a specific action on a specific resource.
The security mistake this distinction exists to prevent — and it's one of the most common real-world vulnerabilities: implementing authentication carefully and then forgetting authorization. A user logs in successfully (authentication works), navigates to `/orders/1234`, changes the number to `1235`, and sees somebody else's order. The system verified who they were and never checked whether they were allowed. This class of bug has caused a very large share of real data breaches, and it's easy to write because the code looks like it's doing something.
The rule that prevents it: authorization must be checked on the server, per request, for the specific resource. Hiding a button in the UI is not authorization — it's a nicety for the user experience. Anyone can send the request directly with a tool that never renders your interface. Every endpoint must independently verify that this authenticated user may perform this action on this record.
A few practical notes:
- Common authorization models: role-based (admin, editor, viewer), ownership-based (you can edit rows where `userId` matches yours), and attribute/policy-based for complex cases. Most applications need ownership checks plus a couple of roles, and nothing more.
- Never trust a role or user id sent from the client. Read it from the verified session on the server.
- Fail closed: if the check can't be evaluated, deny. Defaulting to allow is how permission bugs turn into breaches.
Shorthand that sticks: authentication = identity, authorization = permission. AuthN and AuthZ are the abbreviations you'll see in code and docs, and they're worth recognising because they're deliberately distinguished for exactly this reason.