BookStackApp/BookStack · error · ApiAuthException

errors.api_cookie_auth_only_get

Error message

errors.api_cookie_auth_only_get

What it means

This BookStack API-auth middleware throws ApiAuthException (HTTP 403) when a request is authenticated via the session cookie rather than an API token and the HTTP method is not GET. Cookie-based API access is deliberately limited to read-only GET requests for security (CSRF risk); any POST/PUT/PATCH/DELETE from a cookie session is rejected.

Source

Thrown at app/Http/Middleware/ApiAuthenticate.php:43

    /**
     * Ensure the current user can access authenticated API routes, either via existing session
     * authentication or via API Token authentication.
     *
     * @throws ApiAuthException
     */
    protected function ensureAuthorizedBySessionOrToken(Request $request): void
    {
        // Use the active user session already exists.
        // This is to make it easy to explore API endpoints via the UI.
        if (session()->isStarted()) {
            // Ensure the user has API access permission
            if (!$this->sessionUserHasApiAccess()) {
                throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
            }

            // Only allow GET requests for cookie-based API usage
            if ($request->method() !== 'GET') {
                throw new ApiAuthException(trans('errors.api_cookie_auth_only_get'), 403);
            }

            return;
        }

        // Set our api guard to be the default for this request lifecycle.
        auth()->shouldUse('api');

        // Validate the token and its users API access
        auth()->authenticate();
    }

    /**
     * Check if the active session user has API access.
     */
    protected function sessionUserHasApiAccess(): bool
    {
        $hasApiPermission = user()->can(Permission::AccessApi);

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Obtain an API token (User Profile > API Tokens) and send header 'Authorization: Token <id>:<secret>' on the request
  2. Change the request to a GET if a read was intended (check HTTP method, e.g. fetch defaults)
  3. If calling from your own frontend, route mutations through a server-side proxy that authenticates with a token
  4. Verify no middleware/proxy strips the Authorization header, causing fallback to cookie auth

Example fix

// before
fetch('/api/books', { method: 'POST', credentials: 'include', body })
// after
fetch('/api/books', { method: 'POST', headers: { 'Authorization': 'Token ' + id + ':' + secret, 'Content-Type': 'application/json' }, body })
Defensive patterns

Strategy: try-catch

Validate before calling

const isCookieAuth = !headers.get('Authorization')?.startsWith('Token ');
if (isCookieAuth && method !== 'GET') {
  throw new Error('Cookie-based API auth only allows GET requests; use a token for mutations.');
}

Type guard

function usesTokenAuth(headers) {
  return /^Token\s+[\w-]+:[\w-]+$/.test(headers.get('Authorization') ?? '');
}

Try / catch

try {
  const res = await fetch(url, options);
  if (res.status === 403) {
    const body = await res.json();
    if (body?.message?.includes('api_cookie_auth_only_get')) {
      // switch to token auth or change method to GET
    }
  }
} catch (e) { /* network error handling */ }

Prevention

When it happens

Trigger: A logged-in browser session (session cookie) calls any non-GET API endpoint, e.g. a fetch() with method POST/PUT/DELETE while relying on the session cookie instead of an API token with header Authorization: Token <id>:<secret>.

Common situations: Frontend scripts reusing the logged-in session cookie to mutate data; API clients that never obtained a token; proxies/SDKs stripping the Authorization header so the request falls back to cookie auth; testing mutations in a browser console while logged in.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/53b2378b981bc172. Report an issue: GitHub.