theonedev/onedev · error · UnauthorizedException

Authentication required

Error message

Authentication required

What it means

When the same lookup fails (project not found or inaccessible) but SecurityUtils.getUser() is null — i.e. the request carries no authentication at all — GitFilter throws UnauthorizedException("Authentication required") instead of entity-not-found. This steers anonymous clients toward authenticating rather than leaking project existence.

Source

Thrown at server-core/src/main/java/io/onedev/server/git/GitFilter.java:111

	
	private Long getProjectId(String projectPath, boolean clusterAccess, boolean upload) {
		var facade = projectService.findFacadeByPath(projectPath);
		if (facade == null && projectPath.endsWith(".git")) {
			projectPath = StringUtils.substringBeforeLast(projectPath, ".");
			facade = projectService.findFacadeByPath(projectPath);
		}
		if (StringUtils.isBlank(projectPath))
			throw new ExplicitException("Project not specified");
		if (facade == null) 
			reportProjectNotFoundOrInaccessible(projectPath);
		return facade.getId();
	}

	private void reportProjectNotFoundOrInaccessible(String projectPath) {
		if (SecurityUtils.getUser() != null)
			throw new EntityNotFoundException("Project not found or inaccessible: " + projectPath);
		else
			throw new UnauthorizedException("Authentication required");
	}

	private void doNotCache(HttpServletResponse response) {
		response.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
		response.setHeader("Pragma", "no-cache");
		response.setHeader("Cache-Control", "no-cache, max-age=0, must-revalidate");
	}

	protected void processPack(final HttpServletRequest request, final HttpServletResponse response) 
			throws IOException, InterruptedException, ExecutionException {
		boolean upload = GitSmartHttpTools.isUploadPack(request);
		
		String pathInfo = getPathInfo(request);	
		String service = StringUtils.substringAfterLast(pathInfo, "/");
		String projectInfo = StringUtils.substringBeforeLast(pathInfo, "/");

		String principal = (String) SecurityUtils.getSubject().getPrincipal();
		boolean clusterAccess = SecurityUtils.isSystem(principal);

View on GitHub (pinned to d44925c47c)

Solutions

  1. Authenticate: embed a valid access token or use a credential helper (git config credential.helper) before cloning
  2. Regenerate an expired/revoked access token and update the remote
  3. If the project is meant to be public, grant anonymous read access in project settings
  4. Run git fetch manually to trigger the credential prompt and store credentials

Example fix

// before
git clone https://onedev.example.com/privateproject.git

// after
git clone https://oauth2:<access-token>@onedev.example.com/privateproject.git
Defensive patterns

Strategy: validation

Validate before calling

if (!accessToken || isExpired(accessToken)) {
  throw new Error('Provide a valid OneDev access token before git operations');
}

Try / catch

try {
  git.clone(url);
} catch (UnauthorizedException e) {
  if (e.getMessage().equals("Authentication required")) {
    promptForCredentialsAndRetry();
  }
}

Prevention

When it happens

Trigger: Any git smart-HTTP request (refs/pack) from an unauthenticated session whose project path does not resolve; typical when credentials are absent, the access token expired, or the credential helper stopped supplying credentials.

Common situations: Clone/fetch from CI without configured credentials; expired or revoked access token in the remote URL; credential helper not initialized; anonymous access disabled on the server while the project is not public.

Understand the failure class

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/88f94f4fb85dd55c. Report an issue: GitHub.