toeverything/AFFiNE · error · AuthenticationRequired

authentication_required

authentication_required

Error message

You must sign in first to access this resource.

What it means

The global AuthGuard rejects any route that is not decorated @Public() when no authenticated user could be resolved. signIn() tries the Authorization: Bearer JWT path first (only if the bearer looks like a JWT) and then the affine_session cookie; if neither yields a session, AuthenticationRequired (authentication_required, HTTP 401) is thrown before the handler runs.

Solutions

  1. Sign in first, or attach Authorization: Bearer <auth-session access token> to the request
  2. For cookie auth always send credentials (fetch credentials: 'include' / axios withCredentials: true)
  3. Handle 401 in an interceptor: refresh the session, then retry once or redirect to sign-in
  4. If sessions died after a server config change, check that the auth secret / cookie settings are stable across restarts

Example fix

// before
await fetch('/api/auth/sessions'); // no credentials

// after
const res = await fetch('/api/auth/sessions', {
  credentials: 'include',
  headers: accessToken ? { authorization: `Bearer ${accessToken}` } : {},
});
if (res.status === 401) location.href = '/sign-in';
Defensive patterns

Strategy: try-catch

Type guard

function isAuthenticationRequired(e: unknown): boolean {
  return (
    typeof e === 'object' && e !== null &&
    (e as { code?: string }).code === 'authentication_required'
  );
}

Try / catch

try {
  return await api.get('/auth/sessions');
} catch (e) {
  if (isAuthenticationRequired(e)) {
    redirectToSignIn(); // or refresh session then retry once
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling a protected REST endpoint or GraphQL mutation with no cookies and no bearer token; an expired or revoked affine_session cookie; a bearer string that is not JWT-shaped (falls through to the cookie path); cross-origin fetch that did not send cookies; WebSocket/SSE handshake without credentials.

Common situations: Session expired while the tab was open; server restart with a new auth secret invalidating cookies; frontend calling the API before sign-in finishes; axios/fetch missing withCredentials; devtools 'copy as fetch' losing the cookie header.

Related errors


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

Appendix: source

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

  async canActivate(context: ExecutionContext) {
    const { req, res } = getRequestResponseFromContext(context);
    const clazz = context.getClass();
    const handler = context.getHandler();
    // 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 591f874dad)