{"record":{"id":"5344c73816da06a1","repo":"BookStackApp/BookStack","slug":"errors-api-bad-authorization-format","errorCode":null,"errorMessage":"errors.api_bad_authorization_format","messagePattern":"errors\\.api_bad_authorization_format","errorType":"exception","errorClass":"ApiAuthException","httpStatus":null,"severity":"error","filePath":"app/Api/ApiTokenGuard.php","lineNumber":110,"sourceCode":"            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        }\n\n        if (!Hash::check($secret, $token->secret)) {\n            throw new ApiAuthException(trans('errors.api_incorrect_token_secret'));\n        }","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Api/ApiTokenGuard.php#L92-L128","documentation":"ApiTokenGuard::validateTokenHeaderValue throws ApiAuthException('errors.api_bad_authorization_format') when the Authorization header is present but malformed. BookStack API auth requires the header to start with 'Token ' followed by '<id>:<secret>'; the guard enforces the presence of the ':' separator and the 'Token ' prefix before attempting token lookup. It protects against parsing garbage or unsupported auth schemes (e.g. Bearer) downstream.","triggerScenarios":"Sending an Authorization header without the 'Token ' prefix (e.g. 'Bearer abc123'); omitting the ':' separator between token id and secret (e.g. 'Token abc123xyz' with no id); sending an empty scheme or an entirely different credential format.","commonSituations":"Developers copying a Bearer-token pattern from other APIs; pasting only the token secret instead of '<id>:<secret>'; missing the space after 'Token'; clients that strip or mangle the Authorization header through a proxy.","solutions":["Format the header exactly as 'Authorization: Token <token_id>:<token_secret>' using the id and secret from the user's API token page","Verify there is a single space after 'Token' and a colon separating id and secret","If using an HTTP client, confirm no middleware/proxy rewrites the Authorization header","Regenerate the token if unsure of its components, since the secret is only shown once"],"exampleFix":"// before\n$client->withHeaders(['Authorization' => 'Bearer ' . $secret])->get($url);\n// after\n$client->withHeaders(['Authorization' => 'Token ' . $tokenId . ':' . $secret])->get($url);","handlingStrategy":"validation","validationCode":"// PHP caller-side check before sending\n$hdr = 'Token ' . $tokenId . ':' . $secret;\nif (!str_starts_with($hdr, 'Token ') || substr_count($hdr, ':') !== 1) {\n    throw new InvalidArgumentException('Authorization header must be \"Token <id>:<secret>\"');\n}","typeGuard":"function isValidAuthHeader(string $header): bool {\n    return str_starts_with($header, 'Token ') && str_contains(substr($header, 6), ':');\n}","tryCatchPattern":"try {\n    $res = $client->get($url, ['headers' => ['Authorization' => $hdr]]);\n} catch (ClientException $e) {\n    if ($e->getResponse()->getStatusCode() === 401) {\n        // rebuild header as 'Token <id>:<secret>' and retry once\n    }\n    throw $e;\n}","preventionTips":["Always build the header as 'Token <id>:<secret>' from two separate config values","Never mix Bearer/Basic schemes with BookStack API auth","Trim whitespace from id and secret before composing the header","Unit-test your auth-header builder once and reuse it everywhere"],"tags":["api","authentication","bookstack"],"backgroundTag":"malformed-authorization-header","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}