netbirdio/netbird · error

token received is empty

Error message

token received is empty

What it means

validateTokenAudience was handed an empty token string: the flow had nothing to check. The token to use is the id_token when UseIDToken is set, otherwise the access token, so the emptiness points at which piece the IdP failed to return.

Source

Thrown at client/internal/auth/util.go:34

	if err != nil {
		return "", fmt.Errorf("could not generate %d random bytes: %v", count, err)
	}

	return hex.EncodeToString(buf), nil
}

// validateTokenAudience checks that the token is a well-formed JWT whose
// audience claim matches the expected audience.
//
// It does NOT verify the token's cryptographic signature and therefore must not
// be treated as an authenticity check. The token is obtained by the client
// directly from the IdP token endpoint over TLS, and its signature is verified
// server-side by the management server against the IdP's JWKS
// (see shared/auth/jwt/validator.go). This function is only a client-side
// sanity check that the returned token targets the expected audience.
func validateTokenAudience(token string, audience string) error {
	if token == "" {
		return fmt.Errorf("token received is empty")
	}

	parts := strings.Split(token, ".")
	if len(parts) != 3 {
		return fmt.Errorf("token is not a well-formed JWT")
	}

	claimsString, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return err
	}

	claims := Claims{}
	err = json.Unmarshal(claimsString, &claims)
	if err != nil {
		return err
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Check whether 'Use ID Token' is enabled in the IdP configuration while the flow's scopes lack openid - either add the openid scope or disable UseIDToken.
  2. Capture (at debug level) what the token endpoint returned; an empty token in a 200 response is an IdP application configuration problem.
  3. Re-run the login after fixing scopes in the NetBird IdP configuration.
  4. If the provider cannot issue JWT access tokens at all, switch the configuration to use the id_token.
Defensive patterns

Strategy: validation

Validate before calling

// before starting the flow, confirm the configuration can yield a usable token
func tokenSourcesConfigured(cfg PKCEAuthProviderConfig) error {
    if cfg.UseIDToken && !strings.Contains(cfg.Scope, "openid") {
        return fmt.Errorf("UseIDToken requires the openid scope")
    }
    return nil
}

Type guard

func hasToken(t string) bool { return t != "" }

Try / catch

tokenInfo, err := flow.WaitToken(ctx, info)
if err != nil {
    if strings.Contains(err.Error(), "token received is empty") {
        // the IdP returned no usable token: check UseIDToken + openid scope,
        // or the token endpoint response, before retrying
    }
}

Prevention

When it happens

Trigger: The token endpoint returned a response whose access token is empty, or UseIDToken is enabled and token.Extra("id_token") was absent so IDToken stayed empty while GetTokenToUse() returned the id_token path.

Common situations: IdP application registered without the openid scope so no id_token is minted while UseIDToken is on; a misconfigured token endpoint returning an error body that parses as an empty token; provider returning tokens only in a nonstandard field.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/d109de480d18c19e. Report an issue: GitHub.