Lesson 1 of 12 · 8 min read
Authentication versus Authorization
In this lesson
TL;DR: Authentication answers "who is this?" by verifying a credential: a password plus second factor, a passkey, a certificate, a signed token. Authorization answers "may this principal do this action on this resource?" and must be checked on every request, on the server, against the specific object. Most breaches of access control are authorization failures: a properly logged-in user reads someone else's record. For agents, add a third question: on whose behalf is the action being taken, and with whose permissions?
Before you read
Pick an answer before you read further. The lesson below explains it, and guessing first, right or wrong, helps the explanation stay with you.
1A user logs in correctly, then changes the number in /invoices/1001 to /invoices/1002 and sees another customer's invoice. Did authentication fail?
Two questions, asked in order
Every request that touches protected data needs two answers.
Authentication: who is making this request? The system verifies a credential and establishes a principal, the identity it will act for. For people that means passwords, one-time codes, passkeys or single sign-on. For services it means certificates, signed tokens or cloud workload identities. The result is usually a session or a token that carries the identity to later requests.
Authorization: may this principal do this? Given the principal, the action and the specific resource, the system decides allow or deny. Inputs include roles ("editor"), attributes ("same department", "during business hours") and relationships ("owner of this document", "member of this workspace").
They are different systems with different failure modes. Strong authentication does nothing for a missing authorization check, and perfect authorization logic is worthless if anyone can claim any identity.
How authentication strength is measured
NIST's Digital Identity Guidelines, SP 800-63-4 (final in July 2025, replacing 800-63-3), grade authentication in three authenticator assurance levels and tie each to how often a user must re-authenticate.
| Level | Requires | Re-authenticate at least every | And after inactivity of |
|---|---|---|---|
| AAL1 | One factor | 30 days | (not set) |
| AAL2 | Two factors; a phishing-resistant option must be offered | 24 hours | 1 hour |
| AAL3 | Two factors including a phishing-resistant authenticator with a non-exportable private key in hardware-protected isolation | 12 hours | 15 minutes |
One detail is often asked: syncable authenticators, such as passkeys that synchronise across a user's devices through a cloud account, are not allowed at AAL3, because the key can leave the device. They can meet AAL2.
Where each one fails
| Failure | What happens | Example |
|---|---|---|
| Weak authentication | Attacker becomes someone else | Phished password with no second factor; reused credentials |
| Session or token theft | Attacker reuses a valid identity | Stolen session cookie; bearer token in a log file |
| Missing object-level authorization | Valid user reaches others' data | IDOR: /invoices/1002 returns another customer's invoice |
| Missing function-level authorization | Valid user reaches privileged functions | A non-admin calls /admin/export directly |
| Authorization in the wrong place | Check exists but attacker controls it | Role decided by a hidden form field or by the client app |
| Stale authorization | Access outlives the reason for it | A cached permission still honoured after the user was removed |
OWASP has ranked broken access control as the top web application risk since 2021. It is common because authorization is spread across every endpoint and is easy to forget on one of them, while authentication is usually centralised in one login system.
Three kinds of broken access control in APIs
OWASP's API Security Top 10 (2023) splits authorization failures into three entries, and interviewers like candidates who can tell them apart:
| Entry | What is missing | Example |
|---|---|---|
| API1 Broken Object Level Authorization (BOLA) | A check that this user may access this object | GET /invoices/1002 returns another customer's invoice |
| API3 Broken Object Property Level Authorization (BOPLA) | A check on which fields may be read or written | A profile update accepts "role": "admin" (mass assignment), or a response includes internal fields |
| API5 Broken Function Level Authorization (BFLA) | A check that this user may call this operation at all | A customer calls DELETE /users/{id} meant for administrators |
OWASP's main web Top 10 also kept broken access control at number one in its 2025 edition.
Worked example: an invoice API
An invoice API uses sequential IDs and checks only that the caller is logged in. A customer who owns invoice 1002 requests IDs from 1 to 1,000,000 at 20 requests a second, and finishes in about 14 hours. Every request is authenticated; none is authorized.
A tempting fix is to switch to random UUIDs. That makes enumeration impractical, but any UUID that is shared, logged or leaked in a URL still returns the invoice to anyone. That is obscurity, not authorization. The real fix puts the check in the data access layer, so no code path can forget it: the query loads by ID and tenant and the caller's ownership or role. Then:
- Return 404, not 403, for objects the caller may not see, so the response does not confirm that the ID exists.
- Add a test that requests another tenant's invoice and expects 404, for every HTTP method, not only GET.
- Alert when one user produces many distinct-object 404s per minute.
Switch to random UUIDs
IDs no longer guessable
Query still loads by ID alone
no ownership check
A leaked link opens the invoice
Outcome, unsafe: Obscurity, not authorization
Anyone holding an ID gets the object.
Scope every query to the caller
Load by ID, tenant and owner
Shared repository method
every verb tested
Others' IDs return 404
Outcome, safe: Authorization at the data layer
Guessable or not, the ID alone grants nothing.
Hiding the ID
Unguessable identifiers slow enumeration but leave the missing check in place.
Rules that prevent most bugs
- Authorize on the server, for every request. Never trust the client to hide a button or send a role.
- Check the specific object. "Is this user logged in?" is not authorization. "Does this user own invoice 1002?" is.
- Deny by default. A new endpoint without an explicit rule should reject requests.
- Centralise the decision, not just the login. A shared authorization library or policy service makes checks consistent and testable.
- Re-check on every access. Do not cache "allowed" longer than you can tolerate an outdated answer.
Where the decision is made: PEP and PDP
Access control vocabulary separates two roles. The policy enforcement point (PEP) sits in the request path, asks for a decision and enforces it: an API gateway, middleware or a repository method. The policy decision point (PDP) evaluates policy and answers allow or deny: a library, or a separate service. Keeping them apart lets many enforcement points share one consistent policy, which is how "centralise the decision" works in practice. NIST's Zero Trust Architecture (SP 800-207, 2020) builds on the same split and applies it to every request, not only at a network perimeter.
Three ways to express policy
| Model | Decides by | Strong at | Breaks down when |
|---|---|---|---|
| Role-based (RBAC) | The roles assigned to a user | Coarse duties: who may approve payments | Per-object ownership: roles multiply until there are thousands |
| Attribute-based (ABAC) | Attributes of user, object, action and environment, evaluated against rules | Context: department, data classification, time, device | Rules become hard to reason about and test |
| Relationship-based (ReBAC) | Relationships in a graph: owner of, member of, parent folder of | Sharing and inheritance, as in documents and workspaces | Needs a dedicated, consistent relationship store |
RBAC was formalised by Ferraiolo and Kuhn in 1992 and standardised as ANSI INCITS 359 in 2004. NIST defines ABAC in SP 800-162. ReBAC became widely known through Google's Zanzibar paper (2019), which describes the system behind sharing in Google's products. Most real systems mix them: RBAC for duties, ReBAC for objects, ABAC for context. A sign that a system needs to move on from pure RBAC is role explosion: when the number of roles approaches or exceeds the number of users, the roles are usually encoding relationships or conditions. Access-control models covers this in depth.
Authenticate
Who is calling
Enforcement point
Asks, then enforces
Decision point
Roles, attributes, relations
Data layer
Query scoped to caller
Decision log
Allow and deny
Identity first
Authentication establishes the principal; on its own it permits nothing.
Agents add a third question
When an AI agent acts, three identities can be involved: the user who asked, the agent (a workload with its own credentials), and the resource owner. The safe default is that an agent acting for a user can do at most what that user could do, ideally less: only what this task needs.
Alice
Asks for her report
Agent
Service identity
Records API
Authorizes each call
Alice's data
Also in the picture
Bob's data
Agent uses its own broad identity
If the API authorizes the agent's service account, and that account can read everyone's records, an injected instruction can fetch Bob's data. The API asked the wrong question.
This is the confused deputy problem in its modern form, and it is why delegation standards such as OAuth and token exchange matter for agents.
Writing the three identities into a token
Delegation standards give this a concrete form. OAuth token exchange (RFC 8693) can issue a token whose subject (sub) is the user and whose act claim names the agent acting for them. Access control uses the subject and the current actor; nested act claims only record history. The resource server then checks both: the user may do this, and this agent may do it for them within the task's scope. On the agent's side, the MCP specification (2026-07-28 revision) requires servers to check that a token was issued for them, using the audience defined in RFC 8707, and forbids passing a client's token through to other services. See delegated authorization for agents.
Misconceptions interviewers listen for
- "Logged in means authorized." Authentication establishes identity; the object and action still need a decision.
- "Unguessable IDs are access control." They hide objects; a leaked ID still opens one.
- "RBAC handles ownership." Per-object ownership leads to role explosion; it belongs in relationships or attributes.
- "Hiding a button is function-level authorization." The server must refuse the call itself.
- "An agent with its own service account acts as the user." It acts as itself, often with far more access than the user.
What if · interview follow-ups
Hold your answer as the constraints change
You are reviewing authorization for a multi-tenant invoicing API that also exposes tools to an AI assistant. The interviewer moves from object checks to delegation.
Start here
The API checks ownership on GET but not on PATCH of the same object. What does that tell you?
Per operation, and where checks live.
That checks are scattered per endpoint rather than enforced where data is loaded, so each new verb can forget them. Move the ownership check into the data access layer used by every operation, add field-level rules for what PATCH may change to stop mass assignment, and test every method against another tenant's object.
Carry it forwardAuthorize per operation and per property, enforced at the data layer.
Weak answerAdding the same check to the PATCH handler and moving on.
Constraint changes
A user is removed from a team but keeps access for an hour. Is that acceptable?
Caching and revocation.
Only if someone decided that window deliberately. The delay comes from caching decisions or permissions. Either check on every access, or invalidate caches on revocation events so removal takes effect in seconds, and state the window you accept for each data class.
Carry it forwardStale authorization is a design choice; make the window explicit and small.
Weak answerAccepting whatever the cache lifetime happens to be.
Constraint changes
Roles have grown to 4,000 for 300 users. What is wrong, and what do you change?
What are the roles encoding?
Role explosion: roles are being used to encode per-object relationships and context. Keep RBAC for coarse duties, move ownership and sharing into relationship-based rules, and express context such as data classification or region as attributes, evaluated by one decision point.
Carry it forwardUse roles for duties, relationships for objects and attributes for context.
Weak answerWriting a script to manage the roles more efficiently.
Constraint changes
The assistant calls tools for a user. Whose authorization applies?
Both, intersected.
Both: the user's rights, narrowed to what the task needs. Issue the assistant an exchanged token with the user as subject and the agent as actor, scoped and audience-restricted to the tool's service, and have that service check both. Never let the assistant call tools with a broad service account, which is a confused deputy.
Carry it forwardAn agent gets the user's rights intersected with the task, never more.
Weak answerGiving the agent a service account with access to all tenants.
Constraint changes
The authorization service is down. Should requests fail open or closed?
By risk of the action.
Closed for writes and sensitive reads. For low-risk reads, a short-lived cache of recent decisions can keep the product usable, as an explicit, monitored choice. Whatever the policy, it should be decided in advance, logged when it activates, and never an accident of a timeout handler.
Carry it forwardFail closed by default; any exception is explicit, bounded and monitored.
Weak answerLetting errors default to allow.
Now defend it
As the defender, what would you log and alert on for authorization?
Every deny with the user, action and object; bursts of 404s or denies across many distinct objects from one user; access by users whose permissions were recently revoked; calls to administrative functions by non-administrators; and agent actions recorded with both the user and the agent identity.
Primary references
References for the mechanisms discussed above, accessed 6 November 2026:
How this shows up in interviews
The definitions are a warm-up; the real test comes in design rounds. Interviewers watch whether your design checks ownership of the specific object in code and whether an agent you design authorizes as the user rather than as itself. Saying "the user is authenticated, so they can access it" is a red flag. Saying "the tool resolves the record, checks it belongs to the session's user, and denies otherwise" is what they want to hear.
Say it in 30 seconds: "Authentication is who you are; authorization is what you may do with this specific thing right now. Most access bugs are authorization bugs on logged-in users, like IDOR, so I authorize on the server, per object, on every request, deny by default and centralise the policy. With agents I also ask on whose behalf it's acting, and make it authorize as the user, scoped to the task, never as a broad service account."
Check yourself
Say or write your answer first, then reveal. Explaining it from memory, in your own words, is the skill an interviewer is checking.
1A mobile app hides the 'delete project' button for non-owners. The API endpoint has no check. Is that an authentication or an authorization flaw, and how is it exploited?
2Why is 'the agent can only do what the user can do' a floor rather than a goal?
3Distinguish BOLA, BOPLA and BFLA from the OWASP API Top 10.
4Why are syncable passkeys not allowed at NIST AAL3?