13.1. Authentication vs Authorization
Table of Contents
Understanding Identity and Access
Backend systems constantly answer two questions:
- Who are you?
- What are you allowed to do?
These are the core ideas behind authentication and authorization. They always work together, but they solve different problems and use different mechanisms. As a backend developer you must keep them clearly separated in your mind and in your code.
In this chapter you will learn:
- What authentication is
- What authorization is
- How they work together in a request
- Common examples from real applications
- Typical mistakes beginners make and how to avoid them
What Is Authentication?
Authentication is about proving identity.
When a user or system connects to your backend, the backend needs to know who is making the request. Authentication is the process that answers that question.
Common ways to authenticate:
| Method | Example |
|---|---|
| Password based | Email + password login form |
| Token based | Authorization: Bearer <token> header |
| Session based | Cookie like sessionid=abc123 |
| API keys | X-API-Key: my-secret-key header |
| OAuth 2.0 | "Login with Google" button |
| Certificates | Client TLS certificates between servers |
| One time codes (MFA) | 6 digit code from an authenticator app or SMS |
All of these methods serve a single purpose: let the backend trust that "this request belongs to user X or client Y".
Important rule:
Authentication answers "Who is the caller?" and nothing else.
It does not decide what the caller is allowed to do.
Conceptually, authentication usually has these steps:
- Client sends credentials
For example: username and password in a form, or a JWT in an HTTP header. - Server verifies credentials
- Compare password hash with stored hash
- Validate signature of a JWT
- Look up and check an API key
- Validate a session ID in a session store
- Server attaches identity to the request
In code, you often end up with something like:
request.user = User(id=123, email="alice@example.com", role="admin")- Request continues through the system
Other parts of the backend (authorization checks, business logic) can now userequest.userto decide what is allowed.
If authentication fails, your backend must treat the request as unauthenticated, and usually respond with status code 401 Unauthorized (yes, the name is confusing; it actually means "not authenticated").
What Is Authorization?
Authorization is about access control.
Once you know who is calling, you must decide what they are allowed to do. That decision is authorization.
Typical authorization questions:
- Can this user read this resource?
- Can this user modify or delete this resource?
- Can this user access this endpoint at all?
- Can this user perform this specific action, like "publish", "refund", "ban user"?
Examples:
| Scenario | Authorization decision |
|---|---|
Regular user tries to access /admin/users | Deny, only admins allowed |
| User tries to delete another user’s comment | Deny, only comment owner or admin can delete |
| API client tries to read private data | Allow/deny based on its scopes or permissions |
| Employee tries to update salary table | Allow only HR role |
Authorization usually uses:
- Roles
For example,"admin","user","moderator","editor". - Permissions
For example,"posts:read","posts:edit","orders:refund". - Ownership checks
For example, "is this user the owner of this record?" - Context
For example, time of day, IP address, subscription level, feature flags.
Important rule:
Authorization answers "Is this authenticated caller allowed to do this exact thing?"
It depends on authenticated identity but is a separate step.
Common authorization patterns:
| Pattern | Description |
|---|---|
| Role based (RBAC) | Decide access by user role, for example only admins can access endpoint |
| Permission based (PBAC) | Fine grained permissions attached to users or roles |
| Attribute based (ABAC) | Use attributes, for example department, region, subscription tier |
| Ownership checks | User can access only resources that belong to them |
In HTTP APIs, when authorization fails, you typically use:
- 403 Forbidden
Known user, but not allowed to access this resource or perform this action.
How Authentication and Authorization Work Together
You almost always perform authentication before authorization.
A simplified flow for a protected API endpoint:
- Request comes in
GET /users/123/profile
Authorization: Bearer eyJhbGciOi...- Authentication middleware
- Extract token from header
- Validate signature and expiration
- Decode token and find user in database
- Attach user to request:
request.user = User(id=123, role="user") - Authorization in the route handler or service
- Check if
request.userhas access to this profile - For example, allow if:
request.user.id == 123same user
orrequest.user.role == "admin"admin- Response
- If authentication failed: return
401 - If authentication succeeded but authorization failed: return
403 - If both succeeded: return
200with the requested data
Here is a very simple Python style pseudocode:
def get_user_profile(request, user_id):
# Authentication is usually done by middleware before this
if not request.user: # user is None or anonymous
return Response(status_code=401)
# Authorization: can this user see this profile?
if request.user.id != user_id and request.user.role != "admin":
return Response(status_code=403)
profile = load_profile_from_db(user_id)
return Response(status_code=200, body=profile)Notice how:
- Authentication sets
request.user. - Authorization checks use
request.userto decide access.
Concrete Examples You Already Know
To make the difference more intuitive, consider some everyday examples.
Example 1: Logging in to a Website
- You open
/loginand submit your email and password. - Server checks if the password is correct.
- This is authentication.
- If incorrect, you stay unauthenticated.
- After login, you go to
/admin. - The server checks if your user has role
"admin". - This is authorization.
If your password is correct but you are not an admin:
- Authentication: success
- Authorization: fail
- Result: you cannot access
/admin, even though you are logged in.
Example 2: Viewing Your Bank Account
- You log in with username, password, maybe a one time code.
- Bank verifies your identity authentication.
- You request
/accounts/987654. - Server checks if account
987654belongs to you. - If it belongs to another customer, you are not authorized to see it.
The bank needs both good authentication and good authorization:
- Weak authentication means an attacker can pretend to be you.
- Weak authorization means a logged in user can see or modify accounts they should not.
Example 3: Public vs Private Endpoints
Some endpoints do not require authentication at all. For example:
GET /products
Public product catalog.GET /products/123
Public product details.
Others do:
GET /orders
Only authenticated users can see their own orders.
Flow:
- For public endpoints, your backend may skip authentication and authorization checks.
- For protected endpoints:
- Authentication verifies there is a real user.
- Authorization ensures users see only their own data.
In code, you often declare this explicitly, for example in a framework:
@app.get("/products") # public
def list_products():
...
@app.get("/orders", dependencies=[Depends(auth_required)]) # authentication needed
def list_orders(current_user = Depends(get_current_user)):
...
Inside list_orders, you then apply further authorization checks.
Typical Mistakes Beginners Make
Understanding the difference is important because confusing them leads to security problems and messy code.
Mistake 1: "Logged in" Means "Can Do Anything"
Some beginners think that once a user is authenticated, they can access everything. For example:
def delete_user(user_id, request):
if not request.user:
# Only check if user is logged in
return 401
# Dangerous: allowing any logged in user to delete any account
delete_user_from_db(user_id)
return 200This code is missing authorization.
Correct version should check who is doing this:
def delete_user(user_id, request):
if not request.user:
return 401 # not authenticated
# Only allow admins or the user themself
if request.user.id != user_id and request.user.role != "admin":
return 403 # not authorized
delete_user_from_db(user_id)
return 200Mistake 2: Mixing Authentication Logic with Authorization Logic Everywhere
Another common mistake is to spread both authentication and authorization logic across the whole codebase, with copy pasted checks in every endpoint.
Better approach:
- Authentication in a consistent place
For example, middleware that preparesrequest.user. - Authorization in reusable helpers or decorators
For example, functions likerequire_admin,require_owner,require_permission("orders:refund").
This separation makes code clearer and easier to test.
Mistake 3: Wrong HTTP Status Codes
Backend beginners often return 401 or 403 randomly.
Use them like this:
| Situation | Status code |
|---|---|
| User not authenticated, or missing token | 401 Unauthorized |
| User authenticated but not allowed | 403 Forbidden |
This distinction helps API clients understand what went wrong:
401means "you need to authenticate, or your authentication is invalid".403means "we know who you are, but you are not allowed to do this".
How This Fits with the Rest of the Course
Later chapters in the Authentication section will go deeper into:
- How to store passwords securely
- How to implement login endpoints
- Sessions, cookies, and tokens
- JSON Web Tokens, access tokens, refresh tokens
- OAuth 2.0 and social login
Those topics are all about authentication mechanisms.
The separate Authorization section will later cover:
- Users and permissions
- Role based access control
- Permission based access control
- Protecting API endpoints
- Resource ownership
- Authorization best practices
Those topics are all about authorization strategies.
Summary
To keep in mind:
- Authentication = verifying identity: "Who are you?"
- Authorization = verifying access: "What are you allowed to do?"
- Authentication happens first, authorization depends on its result.
- Return 401 when authentication fails, 403 when authorization fails.
When you design backend endpoints, always think in two steps:
- How will this endpoint know who is calling it?
This is your authentication plan. - Once it knows, how will it decide if this caller can perform this action on this resource?
This is your authorization plan.
Keeping these two concerns clearly separate will make your backends safer, easier to understand, and easier to extend.
Views: 6
KAHIBARO