Authentication (AuthN) proves who a client is; authorization (AuthZ) decides what that client may do. In every correct request flow they run in a fixed order: AuthN first, AuthZ second, never the reverse. Confusing the two produces the two most-misread HTTP status codes in production — 401 Unauthorized (an AuthN failure) and 403 Forbidden (an AuthZ failure). This comparison, evaluated in 2026, breaks down authn vs authz across mechanism, protocols, failure modes, and where offloading either check to a CDN edge actually pays off.
Authentication answers "who are you?" by verifying credentials against a trusted identity source. Authorization answers "what are you allowed to do?" by evaluating that verified identity against a policy. The difference is not academic: they use different protocols, fail with different status codes, and belong in different parts of your architecture.
A request can pass AuthN and still fail AuthZ. You logged in successfully (identity confirmed), then tried to delete a resource you don't own (permission denied). That is a 200-worthy identity paired with a 403 decision.
| Dimension | Authentication (AuthN) | Authorization (AuthZ) |
|---|---|---|
| Question answered | Who are you? | What can you do? |
| Order in the flow | First | Second, only after AuthN |
| Verifies | Identity via credentials | Permissions via policy |
| Common inputs | Passwords, MFA, certificates, passkeys | Roles, scopes, claims, ACLs |
| Typical protocols | OpenID Connect, SAML 2.0, WebAuthn, mTLS | OAuth 2.0 scopes, RBAC, ABAC, XACML |
| Failure response | 401 Unauthorized | 403 Forbidden |
| Changes how often | Rarely (identity is stable) | Often (roles, plans, ownership shift) |
The cleanest operational tell for authn vs authz is the status code: 401 means the system doesn't yet know who you are, 403 means it knows exactly who you are and is refusing anyway.
Authentication collects a credential, checks it against a trusted store, and issues proof of identity — usually a session cookie or an ID token. In OpenID Connect, that proof is an ID token: a signed JSON Web Token (JWT) carrying claims like sub, iss, and exp. WebAuthn and mTLS skip shared secrets entirely and rely on asymmetric keys, which is why they resist phishing.
Authorization takes the verified identity plus context and evaluates it against a policy. Role-based access control (RBAC) maps identities to roles to permissions. Attribute-based access control (ABAC) evaluates attributes — resource owner, time, region, plan tier — at decision time. OAuth 2.0 handles delegated authorization: it lets a third party act with a scoped subset of your permissions without ever seeing your password.
The two checks chain. A client authenticates once, receives a token, then presents that token on every subsequent request where an authorization decision is made. Below is the exchange in two curl calls: first authenticate to get a bearer token, then use it to authorize an API call.
# Step 1 — Authenticate: exchange credentials for an access token
curl -X POST https://auth.example.com/oauth/token \
-d grant_type=client_credentials \
-d client_id=CLIENT_ID \
-d client_secret=CLIENT_SECRET
# CLIENT_ID / CLIENT_SECRET: your registered app credentials
# Step 2 — Authorize: call the API with the bearer token
curl https://api.example.com/v1/orders \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
# Missing/expired token -> 401 Unauthorized (AuthN)
# Valid token, no scope -> 403 Forbidden (AuthZ)
One subtlety trips up most teams: the header is named Authorization, but for bearer tokens it carries authentication proof that a downstream authorization check then reads. The name predates the split we now enforce.
Picking a method is really picking a trade-off between statelessness, revocation speed, and blast radius. The table maps the common options to where each earns its place.
| Method | AuthN / AuthZ | Stateless? | Revocation | Best fit |
|---|---|---|---|---|
| JWT (RFC 7519) | AuthZ token carrier | Yes | Hard — needs short TTL or denylist | APIs, edge validation |
| OpenID Connect | AuthN | No (session-backed) | Session invalidation | User login, SSO |
| SAML 2.0 | AuthN | No | Session invalidation | Enterprise SSO |
| OAuth 2.0 | AuthZ (delegation) | Depends | Token revocation endpoint | Third-party API access |
| mTLS | AuthN | Yes | Certificate revocation | Service-to-service |
| RBAC / ABAC | AuthZ model | N/A | Policy update (instant) | Permission decisions |
Stateless JWTs win on latency and horizontal scale but lose on revocation, which is why short expiry plus a refresh flow beats trying to invalidate a live token.
Authentication and authorization don't have to live only at the origin. A CDN edge can terminate TLS, validate a JWT signature against a cached JWKS, and reject unauthorized requests before they ever consume origin capacity. This splits the work cleanly: coarse checks (is this token signed, unexpired, and scoped correctly?) run at the edge, while fine-grained, data-dependent decisions (does this user own this specific record?) stay at the origin where the data lives.
Validating a JSON Web Token (JWT) signature at a CDN edge node instead of the origin removes one full round trip per request; in 2026 measurements that typically saves 40–120 ms of authorization latency on globally distributed traffic and drops invalid-token requests before they touch backend compute.
An Envoy-style edge filter makes the pattern concrete:
http_filters:
- name: envoy.filters.http.jwt_authn
typed_config:
providers:
main:
issuer: "https://auth.example.com/"
remote_jwks:
http_uri:
uri: "https://auth.example.com/.well-known/jwks.json"
timeout: 5s
cache_duration: { seconds: 300 }
rules:
- match: { prefix: "/v1/" }
requires: { provider_name: "main" }
The trade-off is real. Edge validation only confirms the token is authentic and well-scoped; it cannot know that record 4471 belongs to a different tenant. Push too much authorization logic to the edge and you either duplicate business rules or ship stale policy. Keep ownership and row-level checks at the origin. If you want edge token validation and request-header controls as part of delivery, that behavior is configurable in a modern CDN's edge configuration and request-header controls.
Authentication always comes first. A system must verify identity before it can evaluate what that identity is permitted to do. Running authorization before authentication is a logic error: there is no verified subject to check against a policy. The only exception is anonymous access, where an implicit "public" identity is authorized without an explicit authentication step.
A 401 Unauthorized means authentication failed or is missing — the server does not know who you are. A 403 Forbidden means authentication succeeded but authorization was denied — the server knows exactly who you are and refuses the action. Despite the name "Unauthorized," 401 is an authentication error, which is one of HTTP's most misleading labels.
OAuth 2.0 is an authorization framework, not an authentication protocol. It grants a client scoped access to resources on a user's behalf without exposing credentials. Authentication is layered on top by OpenID Connect, which adds an ID token carrying identity claims. Using raw OAuth 2.0 alone to prove identity is a well-known anti-pattern.
Only in the trivial sense of anonymous or public access, where an unauthenticated request is authorized against a default policy. For any identity-specific decision, authorization requires an authenticated subject first. Attempting per-user permissions without authentication means you are trusting a claimed identity you never verified, which is an open door.
Edge authentication is validating credentials or tokens at the CDN edge before requests reach the origin. Typically the edge verifies a JWT signature against a cached JWKS and rejects invalid or expired tokens early. This cuts origin load and latency, while fine-grained, data-dependent authorization decisions remain at the origin where the underlying data resides.
Pull one hour of production access logs and bucket responses by status code. Separate your 401s from your 403s and graph them independently — most teams discover they've been treating an authentication problem (bad or expired tokens) as an authorization one, or vice versa. Then measure the origin cost of requests that end in 401: every one is a request your edge could have rejected. If that number is non-trivial, prototype JWT signature validation at the edge and re-measure p95 latency and origin CPU. The authn vs authz boundary you draw there is worth getting right before it calcifies.