toeverything/AFFiNE · error · AuthenticationRequired

authentication_required

authentication_required

Error message

You must sign in first to access this resource.

What it means

Thrown by `AuthGuard.canActivate` for a non-`@Public`, non-`@Internal` endpoint when `signIn` resolved to no session (no cookie, no JWT, or both invalid/absent). HTTP 401. This is the primary 'not logged in' gate for the whole HTTP API.

Source

Thrown at packages/backend/server/src/core/auth/guard.ts:115

        }
      }
      throw new AccessDenied('Invalid internal request');
    }

    // api is public
    const isPublic = this.reflector.getAllAndOverride<boolean>(
      PUBLIC_ENTRYPOINT_SYMBOL,
      [clazz, handler]
    );

    const authedUser = await this.signIn(req, res, isPublic);

    if (isPublic) {
      return true;
    }

    if (!authedUser) {
      throw new AuthenticationRequired();
    }

    return true;
  }

  async signIn(
    req: Request,
    res?: Response,
    isPublic = false
  ): Promise<Session | null> {
    const result = await this.resolveRequestSession(req, res, isPublic);
    return result?.session ?? null;
  }

  private async resolveRequestSession(
    req: Request,
    res?: Response,
    isPublic = false

View on GitHub (pinned to 26c515e050)

Solutions

  1. Authenticate first (sign in / refresh) and reissue the request with the resulting cookie or Bearer token.
  2. Ensure cookies are sent (`credentials: 'include'`) and the SameSite policy permits them.
  3. For native clients, refresh the access token via `/session/refresh` when it expires.

Example fix

// before
fetch('/api/auth/methods'); // 401

// after
fetch('/api/auth/methods', { credentials: 'include' });
// or
fetch('/api/auth/methods', {
  headers: { authorization: `Bearer ${accessToken}` },
});
Defensive patterns

Strategy: validation

Validate before calling

async function ensureAuthed(): Promise<void> {
  const r = await fetch('/api/auth/session', { credentials: 'include' });
  if (r.status === 401) location.href = '/sign-in';
}
await ensureAuthed();
await fetch(protectedUrl, { credentials: 'include' });

Try / catch

try {
  await fetch(protectedUrl, { credentials: 'include' });
} catch (e) {
  // network error; not auth
}
// HTTP 401 handling
if (res.status === 401) { redirectToLogin(); }

Prevention

When it happens

Trigger: Calling any protected route without a session cookie or Bearer JWT, with an expired cookie session that `getUserSessionFromRequest` could not revive, or with a non-JWT Authorization header that `extractTokenFromHeader` rejected.

Common situations: Session expired and the refresh path did not run, the client forgot `credentials: 'include'`, a script/bot hit a protected URL, or a native client used an expired access token without refreshing.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/4a03edc4a6d9032e. Report an issue: GitHub.