rancher/rancher · error

empty access token secret for user %s

Error message

empty access token secret for user %s

What it means

Thrown when the stored token secret for the user was found, but its trimmed string content is empty. The secret exists yet contains no credential, so it cannot be unmarshalled or used as a raw access token. This indicates a corrupt or half-written cache entry rather than a missing one.

Source

Thrown at pkg/auth/providers/keycloakoidc/keycloak_provider.go:209

	if err != nil {
		// If the secret lookup failed for a reason other than NotFound, surface the error.
		if !apierrors.IsNotFound(err) {
			return nil, fmt.Errorf("getting access token for user: %w", err)
		}

		// Secret not found: fall back to the access token stored in ProviderInfo.
		accessToken, ok := token.GetProviderInfo()["access_token"]
		if !ok || strings.TrimSpace(accessToken) == "" {
			return nil, fmt.Errorf("no stored access token found for user %s", token.GetUserID())
		}
		oauthToken = &oauth2.Token{
			AccessToken: strings.TrimSpace(accessToken),
		}
	} else {
		// Secret retrieved successfully. First, try to interpret it as JSON-encoded oauth2.Token.
		stored := strings.TrimSpace(storedOauthToken)
		if stored == "" {
			return nil, fmt.Errorf("empty access token secret for user %s", token.GetUserID())
		}
		if unmarshalErr := json.Unmarshal([]byte(stored), &oauthToken); unmarshalErr != nil || oauthToken == nil {
			// If unmarshalling fails or yields nil, fall back to treating the secret as a raw access token string.
			oauthToken = &oauth2.Token{
				AccessToken: stored,
			}
		}
	}

	// Valid will return false if access token is expired
	if !oauthToken.Valid() {
		// since token is not valid, the TokenSource func used in the Client func will attempt to refresh the access token
		// if the refresh token has not expired
		logrus.Debugf("[generic oidc] RefreshAndUpdateToken: attempting to refresh access token")
	}

	reusedToken, err := oauth2.ReuseTokenSource(oauthToken, oauthConfig.TokenSource(ctx, oauthToken)).Token()
	if err != nil {

View on GitHub (pinned to 932558d4e6)

Solutions

  1. Delete the empty token secret so the code falls back to ProviderInfo['access_token'] or the user re-logs in
  2. Audit the writer path (UpdateToken/TokenMgr.SetSecret) that produced the empty value and guard it against persisting empty tokens
  3. Re-login the affected user to repopulate the cache

Example fix

// before
if stored == "" {
    return nil, fmt.Errorf("empty access token secret for user %s", token.GetUserID())
}
// after: treat empty cache as a cache miss and fall through to the ProviderInfo fallback
if stored == "" {
    accessToken, ok := token.GetProviderInfo()["access_token"]
    if !ok || strings.TrimSpace(accessToken) == "" {
        return nil, fmt.Errorf("no stored access token found for user %s", token.GetUserID())
    }
    oauthToken = &oauth2.Token{AccessToken: strings.TrimSpace(accessToken)}
}
Defensive patterns

Strategy: validation

Validate before calling

// Before triggering refresh, check the cached secret is usable
stored, err := tokenMgr.GetSecret(userID, providerName, nil)
if err == nil && strings.TrimSpace(stored) == "" {
    // Corrupt cache entry: drop it so the fallback path can run
    _ = tokenMgr.DeleteSecret(userID, providerName)
}

Prevention

When it happens

Trigger: TokenMgr.GetSecret succeeds but returns "" (or only whitespace) for the user/authProvider key: a secret created with an empty string value, an UpdateToken call that persisted an empty token, or a manually edited/blanked secret.

Common situations: A previous token-update failed midway and wrote an empty value; someone manually recreated the secret without data; secret restored from backup with redacted content.

Related errors


AI-assisted analysis of rancher/rancher@932558d4e6 (2026-08-16). Data as JSON: /api/errors/b60d2475288d3fd7. Report an issue: GitHub.