{"record":{"id":"083a9d2803a75900","repo":"theonedev/onedev","slug":"not-authenticated-083a9d","errorCode":null,"errorMessage":"Not authenticated","messagePattern":"Not authenticated","errorType":"http","errorClass":"UnauthenticatedException","httpStatus":401,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/rest/AnonymousCheckFilter.java","lineNumber":44,"sourceCode":"\tprivate ResourceInfo resourceInfo;\n\t\n\t@Context\n\tprivate HttpServletRequest request;\n\t\n\t@Inject\n\tpublic AnonymousCheckFilter(SettingService settingService) {\n\t\tthis.settingService = settingService;\n\t}\n\t\n\t@Override\n\tpublic void filter(ContainerRequestContext requestContext) throws IOException {\n\t\tApi api = resourceInfo.getResourceClass().getAnnotation(Api.class);\n\t\tif ((api == null || !api.internal()) && SecurityUtils.isAnonymous() \n\t\t\t\t&& resourceInfo.getResourceClass() != TriggerJobResource.class) { \n\t\t\tString method = request.getMethod();\n\t\t\tif (method.equals(\"POST\") || method.equals(\"DELETE\") || method.equals(\"PUT\") \n\t\t\t\t\t|| !settingService.getSecuritySetting().isEnableAnonymousAccess()) {\n\t\t\t\tthrow new UnauthenticatedException();\n\t\t\t}\n\t\t}\n\t}\n\n}\n","sourceCodeStart":26,"sourceCodeEnd":50,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/rest/AnonymousCheckFilter.java#L26-L50","documentation":"AnonymousCheckFilter is a JAX-RS container request filter guarding REST resources. If the resource class lacks @Api(internal=true), the requester is anonymous, and the resource is not TriggerJobResource, it rejects state-changing methods (POST/DELETE/PUT) and also GET etc. when anonymous access is disabled in security settings, by throwing UnauthenticatedException 'Not authenticated'.","triggerScenarios":"Anonymous HTTP calls to REST resources that are POST/DELETE/PUT, or any method when anonymous access is disabled, on non-internal resource classes (everything except @Api(internal=true) and TriggerJobResource).","commonSituations":"CI scripts or webhooks calling the API without an access token; users who disabled anonymous access in security settings but still use unauthenticated GETs; missing Authorization header / expired access token; reverse proxy stripping auth headers.","solutions":["Add an Authorization header with a valid OneDev access token (login/password or access token) to the request","Enable anonymous access in security settings only if the resource is intentionally public (not recommended for writes)","Use GET instead of POST/DELETE/PUT for anonymous read-only endpoints when anonymous access is enabled","Verify the resource is meant to be internal-only; if calling an internal @Api(internal=true) resource is intended, that path is exempt"],"exampleFix":"// before\ncurl -X POST http://onedev/api/projects\n// after\ncurl -X POST -H \"Authorization: Bearer <access-token>\" http://onedev/api/projects","handlingStrategy":"try-catch","validationCode":"// before calling the API, ensure credentials are configured\nif (accessToken == null) {\n    throw new IllegalStateException(\"OneDev access token required for this endpoint\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    Response resp = client.target(url).request()\n        .header(\"Authorization\", \"Bearer \" + token)\n        .post(entity);\n} catch (NotAuthorizedException e) {\n    // refresh/obtain access token and retry once\n}","preventionTips":["Always send Authorization header with a valid access token for API writes","Regenerate tokens after rotation or expiry","Do not rely on anonymous access for POST/PUT/DELETE","Verify proxy/gateway is not stripping the Authorization header"],"tags":["rest","authentication","security"],"backgroundTag":"authentication-required","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}