← Debugging Guides

阅读中文版本

HTTP 403 Forbidden — authenticated but not allowed

Diagnose HTTP 403 Forbidden by separating authentication from authorization, checking roles, scopes, IP rules, and WAF blocks.

intermediatedebugginghttpauthorizationsecurity

Symptoms

  • HTTP 403 Forbidden on an endpoint that worked before
  • The user is logged in yet cannot access a resource
  • The error appears from some networks but not others

Possible causes

  • The authenticated user lacks the required role or scope
  • A WAF or IP allowlist blocks the request
  • Object-level ownership checks reject the resource
  • The token is valid but missing the scope needed for this route

Diagnosis steps

Confirm who the request authenticated as

log the subject and claims in the auth middleware

Interpretation

If a subject is present, authentication succeeded and the failure is authorization.

Check the required role or scope for the route

Interpretation

Comparing the route policy with the token scopes shows the missing capability.

Test from another network or bypass the WAF

Interpretation

A network-dependent 403 indicates IP filtering rather than application policy.

Root cause

A 403 means the server understood the request and knows who you are but refuses to perform it. Unlike 401, providing credentials again will not help; the identity is valid but lacks permission, is blocked by network policy, or fails an ownership check. Many WAFs also return 403 for blocked patterns.

Fix

Grant the missing role or scope, or correct the ownership check so legitimate owners pass. Review allowlists and WAF rules for false positives, and return a distinct code or message so clients can distinguish authorization denial from a network block. Keep authorization decisions centralized so every route enforces the same policy.

Verify the fix

Repeat the request as the same authenticated user; it returns the expected success code, and the auth middleware log shows the required role or scope being satisfied.

Symptoms

An authenticated user receives 403 Forbidden. The credentials are clearly valid, so re-logging in does not help.

Diagnosis

Log the identity and compare to the route policy:

app.use((req, res, next) => {
  console.log('sub=', req.user?.sub, 'scopes=', req.user?.scopes);
  next();
});

Test from a different network to rule out IP or WAF filtering.

Fix

Grant the capability or fix the ownership check:

requireScope('items:write'); // token lacks this scope -> 403
if (item.ownerId !== req.user.sub) return res.sendStatus(403);

Distinguish the WAF case by its response headers or body.

Verification

Retry as the same user with the corrected role or scope; the endpoint succeeds, and the middleware log confirms the required capability is now present.

Auditing Authorization Gaps

A 403 that appears for a user who previously had access usually means a policy changed underneath them: a scope was renamed, a role lost a permission, or an ownership rule tightened. Treat authorization as configuration you can diff, and log the decision inputs — subject, required capability, and resource owner — so a regression is visible without reproducing it by hand. Object-level checks deserve their own tests, because the same endpoint can return 200 for one record and 403 for another depending only on who owns it, and those cases are easy to miss until a real user hits them.