Authentication vs Authorization on the SA interview
Contents:
Why this question gets asked
If you are interviewing for a systems analyst seat at any product company that handles user data — Stripe, Notion, Linear, DoorDash, Snowflake — the AuthN/AuthZ question shows up within the first hour. It usually arrives disguised: "design login for our admin console", "what happens when a free user clicks an enterprise-only button", or "why does your sequence diagram return a 403 here". The interviewer is checking whether you treat identity and permissions as two separate problems or smear them into one fuzzy "auth" blob.
Smearing them together is the senior-vs-junior tell. A junior says "the user is logged in, so they can see the page". A senior says AuthN proves who the request belongs to, AuthZ decides what that identity is allowed to do, and the two are evaluated at different layers with different failure modes.
The other reason this gets asked is that it touches every diagram you draw on the whiteboard: API gateway, service mesh, token format, session storage, audit log, even row-level security in the warehouse. If you mix up 401 and 403 once, the interviewer flags it and watches whether you self-correct.
Authentication — who are you
Authentication answers a single question: is this request actually coming from the principal it claims to be from? The principal is usually a human user, but it can also be a service account, a workload identity in a Kubernetes cluster, or a partner API key. The output of AuthN is a verified identity claim — something like sub: user_42, email: alice@acme.com, mfa: true.
The factors form the classic security triangle: something you know (password), something you have (TOTP, hardware key), and something you are (biometric). Multi-factor authentication combines at least two. Modern enterprise SSO via SAML or OIDC pushes verification to an identity provider — Okta, Auth0, Microsoft Entra — and the relying party only validates the assertion or ID token.
| AuthN method | Strength | Typical use |
|---|---|---|
| Password only | Weak | Legacy internal tools |
| Password + TOTP | Medium | Most consumer apps |
| Password + hardware key (WebAuthn) | Strong | Admin consoles, banking |
| Passwordless (magic link, passkey) | Strong | Modern B2C, growing in B2B |
| mTLS / workload identity | Strong | Service-to-service |
| OAuth ID token from IdP | Depends on IdP | Federated SSO |
Load-bearing distinction: OAuth 2.0 by itself is not an authentication protocol. It is a delegation framework for authorization. OIDC sits on top of OAuth and adds the ID token that actually identifies the user. Confusing the two on a whiteboard is an instant red flag.
Authorization — what can you do
Once AuthN has produced a verified identity, AuthZ takes over. AuthZ answers can user_42 read order #100? can user_42 create invoices in workspace X? can the billing service call the refunds endpoint? The output is binary: allow or deny, sometimes with a reason code for the audit log.
The crucial property of AuthZ is that it is resource-scoped and action-scoped. The same identity can read a document, be denied edit, and be allowed to share only inside the same workspace. This is why a single "is_admin" boolean falls over the moment your product has more than one tenant.
AuthZ decisions live in two places architecturally. Coarse-grained checks happen at the edge — API gateway, BFF, route middleware — answering "is this endpoint accessible to authenticated users at all". Fine-grained checks happen inside the service, against the resource itself — "does this user own this row". A clean design draws both layers and explains which checks live where.
401 vs 403 and other confusions
The HTTP spec is precise here, and interviewers love precision. 401 Unauthorized means AuthN failed: the token is missing, expired, malformed, or signed by the wrong key. The fix is to log in again. 403 Forbidden means AuthN succeeded but AuthZ denied: the identity is known, but it lacks the required permission. The fix is not to log in again — it is to ask an admin for access or accept the limit.
A surprising number of production APIs return the wrong code. Returning 403 when the token is missing leaks endpoint existence. Returning 401 when the user lacks a role bounces clients through a login flow that resolves nothing. The mnemonic that survives interviews: 401 is about the credential, 403 is about the permission.
Two more codes worth knowing. 429 Too Many Requests is rate-limiting, not authorization — conflating it is another junior tell. 402 Payment Required is the right code when a free-tier user hits a paid feature.
Permission models: ACL, RBAC, ABAC, ReBAC
The four models you should be able to compare on demand:
| Model | Granularity | Best fit | Failure mode |
|---|---|---|---|
| ACL (access control list) | Per-resource, per-user | Small file systems, S3 buckets | Explodes at scale, hard to audit |
| RBAC (role-based) | Per-role, roles assigned to users | SaaS apps, admin consoles | Role explosion, "manager-with-export" anti-pattern |
| ABAC (attribute-based) | Policy over attributes (user, resource, env) | Healthcare, finance, regulated | Policy debugging is brutal |
| ReBAC (relationship-based) | Graph of relationships | Notion, Figma, Google Docs | New tooling, smaller ecosystem |
RBAC is the default answer for most product interviews because it is what 80% of B2B SaaS actually ships. You define roles (viewer, editor, admin, billing-owner), attach permissions to roles, and assign users to roles inside a tenant. The interview follow-up is always the same: what do you do when one user needs 95% of the editor role plus one admin-only action? The wrong answer is to create a new role for every variation. The right answer is to acknowledge role explosion as a real cost and either move to ABAC for that slice or model the exception as a per-resource grant.
ABAC evaluates policies like "allow read if user.department == resource.department AND time.hour BETWEEN 9 AND 18". It scales conceptually but is operationally heavy: you need a policy engine (OPA, Cedar, Casbin) and someone who can debug a deny. ReBAC, popularized by Google's Zanzibar paper and tools like SpiceDB and OpenFGA, models permissions as graph relationships — user U is editor of doc D because U is member of team T. It fits collaborative products where sharing happens through invites.
Gotcha: the interviewer's favorite follow-up is "what is the difference between authorization and entitlements?" Entitlements are the commercial layer — what the customer's plan allows — and they often gate features before AuthZ ever runs. Free plan blocks the endpoint with 402 Payment Required; AuthZ would block it with 403. Many systems collapse the two, but the cleanest designs keep them separate.
Implementation patterns
The reference flow: a user signs in through OIDC with an identity provider. The IdP returns an ID token (JWT with identity claims) and an access token. The backend validates the JWT signature against the IdP's JWKS endpoint, extracts the sub claim, and either creates a server-side session or forwards the token on subsequent calls.
# Pseudocode for a typical FastAPI middleware
from fastapi import Depends, HTTPException
def require_auth(token: str = Depends(oauth2_scheme)) -> Identity:
try:
claims = jwt.decode(token, jwks, audience=AUD, issuer=ISS)
except jwt.InvalidTokenError:
raise HTTPException(401, "invalid token")
return Identity(user_id=claims["sub"], roles=claims.get("roles", []))
def require_role(role: str):
def checker(identity: Identity = Depends(require_auth)):
if role not in identity.roles:
raise HTTPException(403, "insufficient permissions")
return identity
return checker
@app.get("/admin/users")
def list_users(_: Identity = Depends(require_role("admin"))):
return db.users.all()AuthN and AuthZ are separate decorators that compose. Invalid token returns 401, missing role returns 403, and the handler never has to think about either. The whiteboard diagram shows the same split: an edge layer that validates tokens and a per-route layer that evaluates roles.
For service-to-service traffic inside a mesh, the pattern is mTLS plus SPIFFE identities, with policy enforced by the sidecar (Envoy, Linkerd-proxy). For external API consumers, API keys for AuthN and scopes for AuthZ dominate.
JWT design: keep tokens short-lived (5-15 minute access tokens, longer refresh tokens stored httpOnly). Sign with asymmetric keys (RS256, ES256) so verifiers do not need the secret. Put only stable identity claims in the token — sub, aud, iss, exp, maybe roles — and look up everything else fresh. Stale roles in a long-lived token is one of the most common security bugs shipped to production.
Common pitfalls
The most damaging mistake is using AuthN status as a proxy for AuthZ. Code that reads if user.is_authenticated: show_admin_panel() works for a single-tenant prototype and breaks the moment you add roles. The fix is to make AuthZ a separate, explicit check after AuthN — every endpoint declares what permission it requires, and middleware enforces it.
Another trap is trusting JWT claims without verifying signatures. A surprising amount of code calls jwt.decode without supplying the public key, silently returning the payload of any well-formed token. The fix is a library that fails closed, pinned issuer and audience claims, and integration with the IdP's JWKS endpoint so rotation works automatically.
Stale permission caches cause subtle bugs. If a manager revokes Alice's editor role at 14:00 but her JWT was issued at 13:55 with a 1-hour expiry, she keeps editing until 14:55. For most apps this is acceptable; for billing, refunds, or PII access it is not. The fix is a revocation list, short-lived tokens with refresh, or a fast permission-check path on sensitive endpoints. Pick the trade-off explicitly on the whiteboard.
A subtler pitfall is missing object-level authorization. Route-level checks confirm "user can call GET /orders/:id" but forget "user owns order :id". This is the IDOR (insecure direct object reference) class of bug — OWASP Top 10. The fix is to push the check into the data layer (row-level security, query-time filters) or wrap repository calls with a decorator that takes both identity and resource.
The last pitfall is logging tokens or password equivalents. Access tokens belong in Authorization headers, not URL query strings, log lines, or error reports. If a token shows up in a Datadog log, you rotate everything that signed it. Build redaction into your logging middleware from day one.
Related reading
- API Gateway vs BFF — Systems Analyst Interview
- Cache strategies — Systems Analyst Interview
- ACID isolation levels — Systems Analyst Interview
- SQL injection and XSS — Systems Analyst Interview
- Kafka — Systems Analyst Interview
If you want to drill the systems analyst loop on questions like this one — AuthN/AuthZ, sequence diagrams, REST design, idempotency keys — NAILDD ships interview practice across exactly this surface area.
FAQ
Is OAuth authentication or authorization?
OAuth 2.0 is an authorization framework — it lets a user delegate access to resources without sharing a password. Authentication on top of OAuth is provided by OIDC (OpenID Connect), which adds the ID token with identity claims. If asked "is OAuth how you log in users?", the answer is "OAuth handles delegation; the OIDC ID token handles identity". Conflating the two is the most common knowledge gap in this question family.
When do I use RBAC vs ABAC?
Use RBAC when the model is mostly "what role does this user have inside this tenant", and roles stay manageable (under a few dozen). Use ABAC when decisions depend on context that does not fit cleanly into roles — time of day, device posture, resource sensitivity, regulatory class. Most B2B SaaS ships RBAC with a few ABAC rules glued on; pure ABAC is heavier and shows up in healthcare, finance, and government.
What is the right JWT expiry?
For access tokens, 5 to 15 minutes is the modern consensus — short enough that revocation lag is acceptable, long enough that refresh traffic is not painful. Refresh tokens live longer (hours to days), bound to a session, and revocable server-side. Never put a 24-hour expiry on an access token unless your threat model is "no one will ever steal a token".
How do I handle authorization for shared resources like documents?
This is where ReBAC shines. Model the permission as a relationship — user U has edit on doc D — possibly through intermediates (user is member of team, team has access to doc). SpiceDB, OpenFGA, and Permify implement Zanzibar-style graphs at scale. For simpler products, a relational schema with (resource_id, principal_id, permission) rows and recursive CTEs for inheritance is the pragmatic starting point.
What is the principle of least privilege and how do I prove it in design?
Least privilege means each identity gets the minimum permissions required, no more. In a design review you prove it by walking the role/policy matrix and showing no role bundles unrelated permissions. Concrete signals: scoped API keys instead of root keys, per-environment service accounts, separate roles for "read PII" and "export PII", and an audit log that records every permission check. Reviewers remember the candidate who voluntarily brings up audit logging.
Where does authorization sit relative to rate limiting and entitlements?
The clean stack from edge to handler: TLS → rate limit → AuthN → entitlements → AuthZ → handler. Rate limiting protects the service before identity is known. Entitlements enforce the commercial plan. AuthZ enforces per-identity permission. Returning the right status at each layer — 429, 402, 401, 403 — gives clients clean signals and operators clean dashboards.