{"record":{"id":"87a978f50a8ef2f0","repo":"unclecode/crawl4ai","slug":"token-issuance-is-disabled-no-api-token-is-config","errorCode":null,"errorMessage":"Token issuance is disabled: no api_token is configured on the server.","messagePattern":"Token issuance is disabled: no api_token is configured on the server\\.","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"deploy/docker/server.py","lineNumber":540,"sourceCode":"\n@app.exception_handler(Exception)\nasync def _unhandled_exception_handler(request: Request, exc: Exception):\n    cid = _uuid.uuid4().hex[:12]\n    logger.exception(\"unhandled exception [cid=%s]\", cid)\n    return JSONResponse(\n        {\"error\": \"Internal server error\", \"correlation_id\": cid},\n        status_code=500,\n    )\n\n\n# ──────────────────────── Endpoints ──────────────────────────\n@app.post(\"/token\")\nasync def get_token(req: TokenRequest):\n    expected_token = config.get(\"security\", {}).get(\"api_token\", \"\")\n    if not expected_token:\n        # Fail closed: without a configured api_token the old behavior minted a\n        # JWT to anyone whose email merely had an MX record. Refuse instead.\n        raise HTTPException(\n            403,\n            \"Token issuance is disabled: no api_token is configured on the server.\",\n        )\n    if not req.api_token or not constant_time_eq(req.api_token, expected_token):\n        raise HTTPException(401, \"Invalid or missing api_token\")\n    if not verify_email_domain(req.email):\n        raise HTTPException(400, \"Invalid email domain\")\n    token = create_access_token({\"sub\": req.email})\n    return {\"email\": req.email, \"access_token\": token, \"token_type\": \"bearer\"}\n\n\n@app.post(\"/config/dump\")\nasync def config_dump(\n    data: dict,\n    _td: Dict = Depends(token_dep),\n):\n    try:\n        return JSONResponse(_config_from_json(data))","sourceCodeStart":522,"sourceCodeEnd":558,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/server.py#L522-L558","documentation":"An explicit 403 raised by POST /token when the server has no security.api_token configured. Token issuance fails closed: previously the endpoint minted a JWT to anyone whose email domain merely had an MX record, so in the current design the absence of a configured api_token disables issuance entirely rather than falling back to weak verification.","triggerScenarios":"Calling POST /token with any credentials on a server whose config lacks the security.api_token key (fresh install, default config, or a deployment that intended JWT-only auth without shared-secret bootstrapping).","commonSituations":"New deployments that never set the api_token secret; environments where the token was expected to be injected via env/secret store but was not (secret mount missing); staging servers sharing a config template that omits the security block.","solutions":["Configure security.api_token in the server config (via its config file or the secret-injection mechanism the deployment uses), then restart/reload the server.","Verify with a config-health check or by confirming the secret is mounted before calling /token.","If email-domain-based open issuance was intended, that behavior is intentionally removed - use the configured api_token flow."],"exampleFix":"# before\n# server config (yaml/json) lacks:\n#   security: {}\npost(f\"{base}/token\", json={\"email\": \"a@b.com\", \"api_token\": \"\"})  # 403\n\n# after\n# server config:\n#   security: {\"api_token\": \"<shared-secret>\"}\npost(f\"{base}/token\", json={\"email\": \"a@b.com\", \"api_token\": \"<shared-secret>\"})","handlingStrategy":"validation","validationCode":"def token_issuance_available(base) -> bool:\n    \"\"\"Probe whether the server has an api_token configured.\n    403 from /token means issuance is disabled.\"\"\"\n    r = post(f\"{base}/token\", json={'email': 'probe@example.com', 'api_token': 'x'})\n    return r.status_code != 403","typeGuard":null,"tryCatchPattern":"try:\n    tok = post(f\"{base}/token\", json={'email': email, 'api_token': secret})\nexcept HTTPError as e:\n    if e.response.status_code == 403:\n        raise RuntimeError(\n            'server has no security.api_token configured; ask the operator to set it'\n        ) from e\n    raise","preventionTips":["Configure security.api_token as part of deployment provisioning, not as an afterthought.","Add a startup/smoke check that fails deployment when /token returns 403 in environments that need JWTs.","Treat 403 from /token as a configuration defect to fix server-side - no client workaround exists."],"tags":["fastapi","authentication","configuration","security","token"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}