The names are misleading: 401 Unauthorized is really about authentication, and 403 Forbidden is about authorization.
A 401 must carry a WWW-Authenticate header telling the client how to authenticate. Retrying with a fresh token can fix it. A 403 will fail the same way no matter how many times you log in.
route.ts
if (!session) return new Response(null, { status: 401, headers: { "WWW-Authenticate": "Bearer" } }); if (!can(session.user, "invoice:read", invoice)) return new Response(null, { status: 403 });
Tip
Hiding a resource’s existence? Return 404 instead of 403. GitHub does this for private repositories.