vxcontrol/pentagi · warning

Token.Unauthorized

Token.Unauthorized

Error message

not authorized to access this token

What it means

GetToken performs an authorization check after loading the token: unless the caller holds the 'settings.tokens.admin' permission, a user may only read their own tokens (token.UserID == uid). Otherwise it returns Token.Unauthorized with 'not authorized to access this token'. This prevents IDOR-style access to other users' tokens by ID.

Source

Thrown at backend/pkg/server/services/api_tokens.go:218

	uid := c.GetUint64("uid")
	prms := c.GetStringSlice("prm")
	tokenID := c.Param("tokenID")

	var token models.APIToken
	if err := s.db.Where("token_id = ? AND deleted_at IS NULL", tokenID).First(&token).Error; err != nil {
		logger.FromContext(c).WithError(err).Errorf("error finding token")
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.Error(c, response.ErrTokenNotFound, err)
		} else {
			response.Error(c, response.ErrInternal, err)
		}
		return
	}

	hasAdmin := auth.LookupPerm(prms, "settings.tokens.admin")
	if !hasAdmin && token.UserID != uid {
		logger.FromContext(c).Errorf("user %d attempted to access token of user %d", uid, token.UserID)
		response.Error(c, response.ErrTokenUnauthorized, errors.New("not authorized to access this token"))
		return
	}

	isExpired := token.CreatedAt.Add(time.Duration(token.TTL) * time.Second).Before(time.Now())
	if token.Status == models.TokenStatusActive && isExpired {
		token.Status = models.TokenStatusExpired
	}

	if err := token.Valid(); err != nil {
		logger.FromContext(c).WithError(err).Errorf("error validating token data")
		response.Error(c, response.ErrTokenInvalidData, err)
		return
	}

	response.Success(c, http.StatusOK, token)
}

// UpdateToken updates name and/or status of a token

View on GitHub (pinned to ea665308ba)

Solutions

  1. Log in as the user who owns the token, or have the owner fetch it.
  2. Grant the caller the settings.tokens.admin permission if cross-user token administration is intended.
  3. Verify you are using the correct token ID for the current authenticated user.
  4. If admin access is required, use an account whose role actually includes the settings.tokens.admin permission.

Example fix

// before
curl /tokens/77  // token 77 owned by user 5, caller is user 9 without admin -> 403
// after
curl /tokens/88  // token owned by caller, or caller has settings.tokens.admin
Defensive patterns

Strategy: validation

Validate before calling

const myTokens = await api.get('/tokens');
if (!myTokens.data.tokens.some(t => t.id === tokenId) && !hasPerm('settings.tokens.admin')) {
  throw new Error('this token belongs to another user');
}

Type guard

function canAccessToken(token, uid, perms) { return perms.includes('settings.tokens.admin') || token.user_id === uid; }

Try / catch

try { const t = await api.get(`/tokens/${id}`); }
catch (e) { if (e.response?.data?.code === 'Token.Unauthorized') showNoAccess(); else throw e; }

Prevention

When it happens

Trigger: GET /tokens/{id} where {id} belongs to another user and the caller's permission set lacks settings.tokens.admin.

Common situations: Script iterating token IDs copied from another user/admin session; token ID cached from a previous account; team member trying to inspect a colleague's token without admin grants; role change revoked the admin permission but old scripts still run.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/15d6ad7aa85391f8. Report an issue: GitHub.