{"record":{"id":"07e16ecbfbe8032d","repo":"nextcloud/server","slug":"csrf-check-not-passed","errorCode":null,"errorMessage":"CSRF check not passed.","messagePattern":"CSRF check not passed\\.","errorType":"http","errorClass":"Sabre\\DAV\\Exception\\NotAuthenticated","httpStatus":401,"severity":"error","filePath":"apps/dav/lib/Connector/Sabre/Auth.php","lineNumber":178,"sourceCode":"\n\t\treturn true;\n\t}\n\n\t/**\n\t * @return array{bool, string}\n\t * @throws NotAuthenticated\n\t */\n\tprivate function auth(RequestInterface $request, ResponseInterface $response): array {\n\t\t$forcedLogout = false;\n\n\t\tif (!$this->request->passesCSRFCheck()\n\t\t\t&& $this->requiresCSRFCheck()) {\n\t\t\t// In case of a fail with POST we need to recheck the credentials\n\t\t\tif ($this->request->getMethod() === 'POST') {\n\t\t\t\t$forcedLogout = true;\n\t\t\t} else {\n\t\t\t\t$response->setStatus(Http::STATUS_UNAUTHORIZED);\n\t\t\t\tthrow new \\Sabre\\DAV\\Exception\\NotAuthenticated('CSRF check not passed.');\n\t\t\t}\n\t\t}\n\n\t\tif ($forcedLogout) {\n\t\t\t$this->userSession->logout();\n\t\t} else {\n\t\t\tif ($this->twoFactorManager->needsSecondFactor($this->userSession->getUser())) {\n\t\t\t\tthrow new \\Sabre\\DAV\\Exception\\NotAuthenticated('2FA challenge not passed.');\n\t\t\t}\n\t\t\tif (\n\t\t\t\t//Fix for broken webdav clients\n\t\t\t\t($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED)))\n\t\t\t\t//Well behaved clients that only send the cookie are allowed\n\t\t\t\t|| ($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && empty($request->getHeader('Authorization')))\n\t\t\t\t|| \\OC_User::handleApacheAuth()\n\t\t\t) {\n\t\t\t\t$user = $this->userSession->getUser()->getUID();\n\t\t\t\t$this->currentUser = $user;","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/Connector/Sabre/Auth.php#L160-L196","documentation":"Raised in Sabre\\Auth::auth() for DAV requests that rely on the browser session: passesCSRFCheck() failed and requiresCSRFCheck() is true, and the method is not POST (POST instead takes the forcedLogout path and re-checks credentials). The server sets 401 and throws NotAuthenticated. In practice: a cookie-authenticated DAV request (PROPFIND, GET, PUT, ...) reached the server without a valid request token.","triggerScenarios":"JS or curl calling remote.php/dav/... with only the session cookie, no X-Request-Token header / requesttoken parameter, and no Authorization header — e.g. a fetch() missing the requesttoken header, or a copied curl command reused after the token expired with the session still alive.","commonSituations":"Custom front-end code calling DAV endpoints and forgetting the request token; long-lived sessions whose requesttoken was rendered into an old page; tools that mix cookie auth with WebDAV semantics; requests proxied through middleware stripping custom headers.","solutions":["Send the CSRF token: header 'X-Request-Token' (or 'requesttoken' form/query parameter) using the token embedded in the page (oc_requesttoken / data-requesttoken)","Prefer app-password basic auth for scripted/daemon access — it bypasses CSRF and 2FA entirely","For POST requests the code force-logs-out and retries credential checks; ensure your client re-authenticates rather than looping","Regenerate the page/session if the token is stale (log out and back in)"],"exampleFix":"# before (cookie only -> CSRF check not passed)\ncurl -b nc_cookies.txt -X PROPFIND https://cloud.example/remote.php/dav/files/alice/ -H 'Depth: 0'\n\n# after (app password: no session, no CSRF involved)\ncurl -u alice:xxxx-xxxx-xxxx-xxxx -X PROPFIND https://cloud.example/remote.php/dav/files/alice/ -H 'Depth: 0'","handlingStrategy":"validation","validationCode":"// JS: every cookie-authenticated DAV call must carry the request token\nconst token = document.querySelector('[data-requesttoken]')?.dataset.requesttoken ?? oc_requesttoken;\nawait fetch('/remote.php/dav/files/alice/test.txt', {\n  method: 'PUT',\n  headers: {\n    'X-Request-Token': token,\n    'Content-Type': 'text/plain',\n  },\n  body: data,\n});","typeGuard":null,"tryCatchPattern":"try {\n    await davPut(url, body);\n} catch (e) {\n    if (e.status === 401 && /CSRF check not passed/.test(e.body ?? '')) {\n        // cookie session without valid request token: refresh token from the page or switch to app-password auth\n        await refreshRequestTokenAndRetryOnce();\n    } else {\n        throw e;\n    }\n}","preventionTips":["Attach X-Request-Token (or requesttoken parameter) to every cookie-based DAV request","Use app passwords + basic auth for scripts, daemons, and CLI — CSRF does not apply","Read the token fresh from the current page payload; tokens from cached pages may be stale","Keep auth method consistent per client: either full session+token or full basic auth"],"tags":["dav","authentication","csrf","session","http-401"],"backgroundTag":"csrf-check-failed","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}