gitroomhq/postiz-app · error · HttpForbiddenException
Forbidden
Error message
Forbidden
What it means
The auth middleware requires a JWT on every authenticated request, read from the auth header or the auth cookie. If neither is present it immediately throws HttpForbiddenException (403 'Forbidden') before any JWT verification happens. This is the missing-credentials gate for the dashboard/API.
Source
Thrown at apps/backend/src/services/auth/auth.middleware.ts:37
sameSite: 'none',
}
: {}),
expires: new Date(0),
maxAge: -1,
});
res.header('logout', 'true');
};
@Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(
private _organizationService: OrganizationService,
private _userService: UsersService
) {}
async use(req: Request, res: Response, next: NextFunction) {
const auth = req.headers.auth || req.cookies.auth;
if (!auth) {
throw new HttpForbiddenException();
}
try {
// Verify the JWT signature only. Never trust authorization-relevant
// claims (id, isSuperAdmin, activated) from the token body — always
// re-resolve the user from the database using the id.
const payload = AuthService.verifyJWT(auth) as User | null;
const orgHeader = req.cookies.showorg || req.headers.showorg;
if (!payload?.id) {
throw new HttpForbiddenException();
}
let user = (await this._userService.getUserById(payload.id)) as User | null;
if (!user) {
throw new HttpForbiddenException();
}
View on GitHub (pinned to 0f1647f749)
Solutions
- Attach the JWT: pass it in the auth header or ensure the auth cookie is sent with the request
- For API clients, obtain the token via login and set the header on every request
- For browsers, verify cookies survive the cross-origin setup (credentials: 'include', correct SameSite/CORS config)
- Check that a proxy/gateway is not dropping the auth header or Cookie
Example fix
// before
await fetch(`${API}/posts`, { method: 'GET' }); // 403
// after
await fetch(`${API}/posts`, {
method: 'GET',
headers: { auth: token }, // or rely on credentials: 'include' for cookies
credentials: 'include',
}); Defensive patterns
Strategy: validation
Validate before calling
const auth = getAuthCookie() ?? getStoredToken();
if (!auth) throw new Error('Missing credentials — login before calling authenticated routes');
await api.call({ headers: { auth } }); Type guard
const hasCredentials = (req: { headers: Record<string, unknown>; cookies: Record<string, unknown> }): boolean =>
!!(req.headers?.auth || req.cookies?.auth); Try / catch
try {
await api.get('/protected');
} catch (e: any) {
if (e?.status === 403 && !getAuthCookie()) {
await redirectToLogin(); return;
}
throw e;
} Prevention
- Centralize token attachment in one HTTP client/interceptor so no call forgets it
- For cross-origin browser apps set credentials: 'include' and correct SameSite/CORS
- Never assume cookies carry over between domains or after logout
When it happens
Trigger: Any request to a route guarded by this middleware without an Authorization/auth header and without an auth cookie — e.g. curl/API client forgetting the token, cookie lost after logout or expiry, cross-domain request where cookies are not sent (SameSite), or first request after session clear.
Common situations: Scripts calling authenticated endpoints without attaching the JWT; browser cookie blocked by SameSite/None misconfiguration when frontend and backend are on different origins; session cookie expired or cleared; reverse proxy stripping the auth header.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27).
Data as JSON: /api/errors/8cae0e1320d011af.
Report an issue: GitHub.