{"record":{"id":"26dc080e2eed954d","repo":"BookStackApp/BookStack","slug":"errors-api-no-authorization-found","errorCode":null,"errorMessage":"errors.api_no_authorization_found","messagePattern":"errors\\.api_no_authorization_found","errorType":"exception","errorClass":"ApiAuthException","httpStatus":null,"severity":"error","filePath":"app/Api/ApiTokenGuard.php","lineNumber":106,"sourceCode":"\n        $this->validateToken($token, $secret);\n\n        if ($this->loginService->awaitingEmailConfirmation($token->user)) {\n            throw new ApiAuthException(trans('errors.email_confirmation_awaiting'));\n        }\n\n        return $token->user;\n    }\n\n    /**\n     * Validate the format of the token header value string.\n     *\n     * @throws ApiAuthException\n     */\n    protected function validateTokenHeaderValue(string $authToken): void\n    {\n        if (empty($authToken)) {\n            throw new ApiAuthException(trans('errors.api_no_authorization_found'));\n        }\n\n        if (!str_contains($authToken, ':') || !str_starts_with($authToken, 'Token ')) {\n            throw new ApiAuthException(trans('errors.api_bad_authorization_format'));\n        }\n    }\n\n    /**\n     * Validate the given secret against the given token and ensure the token\n     * currently has access to the instance API.\n     *\n     * @throws ApiAuthException\n     */\n    protected function validateToken(?ApiToken $token, string $secret): void\n    {\n        if ($token === null) {\n            throw new ApiAuthException(trans('errors.api_user_token_not_found'));\n        }","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Api/ApiTokenGuard.php#L88-L124","documentation":"validateTokenHeaderValue() throws ApiAuthException(trans('errors.api_no_authorization_found')) when the extracted authorization token string is empty — i.e. the request carried no usable Authorization header. A companion check for the 'Token ' prefix and ':' separator raises the api_bad_authorization_format error instead.","triggerScenarios":"API request made without an Authorization header at all, or with a header stripped by a proxy/load-balancer, or with a client that sets an empty Authorization value.","commonSituations":"Forgetting to set the Authorization header in an HTTP client; CORS preflight or reverse proxy (nginx/Cloudflare) dropping the Authorization header; using a header name like X-API-Key instead of Authorization; frameworks requiring an explicit header allow-list.","solutions":["Set the Authorization header to 'Token id:secret' on every API request.","Verify with curl -v or the client's debug output that the header is actually sent.","Check proxy/server config (e.g. nginx, Apache mod_headers, Cloudflare) for rules stripping Authorization; add the header to CORS Access-Control-Allow-Headers if applicable.","Ensure you are calling the /api routes (web routes do not use this guard)."],"exampleFix":"// before\ncurl https://example.com/api/books\n// after\ncurl -H \"Authorization: Token abc123:secret456\" https://example.com/api/books","handlingStrategy":"validation","validationCode":"$authToken = $request->header('Authorization');\nif (empty($authToken)) {\n    throw new InvalidArgumentException('Authorization header is required for API calls.');\n}","typeGuard":null,"tryCatchPattern":"try {\n    $response = $client->get($apiUrl);\n} catch (ApiAuthException $e) {\n    if ($e->getCode() === 401) {\n        // inspect that the Authorization header was actually sent\n    }\n    throw $e;\n}","preventionTips":["Always send the Authorization header on API requests; add it to shared client defaults.","Add 'Authorization' to CORS Access-Control-Allow-Headers when browsers call the API.","Audit reverse-proxy configs for header stripping (mod_headers, Cloudflare transforms).","Use 'Authorization', not custom header names like X-API-Key."],"tags":["api","authentication","http-401","missing-header"],"backgroundTag":"missing-authorization-header","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}