goharbor/harbor · error

Unauthorized

Error message

Unauthorized

What it means

BaseController.RequireAuthenticated (src/core/api/base.go:70) returns false and sends HTTP 401 Unauthorized when the request's security context reports an unauthenticated caller. It is the standard gate for core API endpoints; the companion Prepare must already have succeeded (a security context exists, but the caller is anonymous).

Source

Thrown at src/core/api/base.go:70

// Prepare inits security context and project manager from request
// context
func (b *BaseController) Prepare() {
	ctx, ok := security.FromContext(b.Context())
	if !ok {
		log.Errorf("failed to get security context")
		b.SendInternalServerError(errors.New(""))
		return
	}
	b.SecurityCtx = ctx
	b.ProjectCtl = projectcontroller.Ctl
}

// RequireAuthenticated returns true when the request is authenticated
// otherwise send Unauthorized response and returns false
func (b *BaseController) RequireAuthenticated() bool {
	if !b.SecurityCtx.IsAuthenticated() {
		b.SendError(errors.UnauthorizedError(errors.New("Unauthorized")))
		return false
	}
	return true
}

// HasProjectPermission returns true when the request has action permission on project subresource
func (b *BaseController) HasProjectPermission(projectIDOrName any, action rbac.Action, subresource ...rbac.Resource) (bool, error) {
	_, _, err := utils.ParseProjectIDOrName(projectIDOrName)
	if err != nil {
		return false, err
	}

	project, err := b.ProjectCtl.Get(b.Context(), projectIDOrName)
	if err != nil {
		return false, err
	}

	resource := rbac_project.NewNamespace(project.ProjectID).Resource(subresource...)

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Send valid credentials: basic auth for robots/users, or Authorization: Bearer <token> obtained from the token service
  2. Refresh or recreate the robot account token (check its expires_at) and update the secret in CI
  3. Verify the user/robot is active and not locked, and that the auth mode (db_auth/oidc/ldap) matches how the credential was issued

Example fix

# before
curl https://harbor.example.com/api/v2.0/projects   # 401 Unauthorized

# after
curl -u 'robot$ci+drone:<secret>' https://harbor.example.com/api/v2.0/projects
# or
curl -H 'Authorization: Bearer <jwt>' https://harbor.example.com/api/v2.0/projects
Defensive patterns

Strategy: validation

Validate before calling

req.SetBasicAuth(robotName, robotSecret) // or
req.Header.Set("Authorization", "Bearer "+getToken())
// validate robot token expiry before use:
if robotToken.ExpiresAt != nil && robotToken.ExpiresAt.Before(time.Now()) {
    return errors.New("robot token expired; rotate it")
}

Type guard

func isUnauthorized(err error) bool {
    return errors.IsErr(err, errors.UnauthorizedCode)
}

Try / catch

resp, err := client.Do(req)
if err == nil && resp.StatusCode == http.StatusUnauthorized {
    // refresh token / rotate robot secret, then retry once with new credentials
}

Prevention

When it happens

Trigger: Calling any authenticated Harbor API without credentials; using an expired/revoked OIDC or robot token; a robot account whose disabling makes IsAuthenticated() false; bearer token from a different issuer.

Common situations: Expired robot account tokens in CI after the configured TTL; OIDC token refresh missed by a client; curl without -u/-H Authorization against an authenticated endpoint; scripts run after a user was deactivated.

Understand the failure class

Related errors


AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16). Data as JSON: /api/errors/b7fa7caa8c449366. Report an issue: GitHub.