Skip to content

Error reference ​

zero-auth returns structured errors with the same shape for middleware, refresh, and manual token operations.

json
{
  "error": {
    "code": "AUTH_TOKEN_EXPIRED",
    "message": "Authentication token has expired.",
    "statusCode": 401
  }
}

Error codes ​

CodeHTTP statusWhat it meansWhat to check
AUTH_TOKEN_MISSING401No access token was found.Send Authorization: Bearer <token> or configure/send the auth cookie.
AUTH_TOKEN_INVALID401The token is malformed, has the wrong signature, or was revoked.Check that the complete token is sent and that access/refresh secrets match the issuer.
AUTH_TOKEN_EXPIRED401The token passed its expiration time.Refresh the session or sign in again.
AUTH_UNAUTHORIZED401The request is not authenticated.Run protect() before code that needs req.user.
AUTH_FORBIDDEN403The user is authenticated but lacks the required role or permission.Check req.user.role, req.user.permissions, and the authorization middleware.
AUTH_CSRF_INVALID403Auth cookies are present but the CSRF token is missing or invalid.Send the token returned by auth.csrfToken(res) in the configured header and keep the matching CSRF cookie.

Express error handling ​

Register the built-in handler after all routes:

ts
app.use(auth.errorHandler);

This turns AuthError instances into the JSON response shown above.

Manual handling ​

Use isAuthError() when you need to customize logging or the response:

ts
import { isAuthError } from "@0-auth/zero-auth";

try {
  const payload = await auth.verifyToken(token);
  return res.json({ payload });
} catch (error) {
  if (isAuthError(error)) {
    console.warn("Authentication failed", {
      code: error.code,
      statusCode: error.statusCode,
    });
    return res.status(error.statusCode).json(error.toJSON());
  }

  return next(error);
}

Common fixes ​

Every request returns AUTH_TOKEN_MISSING ​

Check the header spelling and ensure the access token is not the refresh token:

http
Authorization: Bearer eyJ...

For cookies, check the cookie path, domain, secure flag, and whether the browser request includes credentials.

Tokens are always invalid ​

Use the same accessSecret when signing and verifying access tokens. Keep the refreshSecret separate and do not use it for access-token requests.

Admin requests return AUTH_FORBIDDEN ​

authorize() checks req.user.role. Make sure the role is included in the token payload and that protect() runs first:

ts
app.get("/admin", auth.protect(), auth.authorize(["admin"]), handler);

For permission checks, confirm every required permission is present:

ts
app.get(
  "/users",
  auth.protect(),
  auth.authorizePermissions(["users:read"]),
  handler,
);

Mount auth.csrf() before the state-changing routes, request a token from a same-origin endpoint, and send the returned value in the configured header. Check that the browser includes credentials and that the CSRF cookie has not been overwritten by a different path or domain.

Refresh requests return AUTH_TOKEN_INVALID ​

With rotation enabled, a refresh token is single-use. Confirm that concurrent clients do not refresh the same token and that the replacement token is saved after a successful response. If the revocation store is unavailable, the handler fails closed and the client should sign in again or retry according to the application's availability policy.