netbirdio/netbird · error

required token field audience is absent

Error message

required token field audience is absent

What it means

The token was a structurally valid JWT and its payload parsed as JSON, but the aud claim is absent (claims.Audience == nil). The check requires an audience because the whole point is confirming the token targets this deployment; an aud-less token cannot be matched against the configured Audience or the ClientID fallback.

Source

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

	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
	}

	if claims.Audience == nil {
		return fmt.Errorf("required token field audience is absent")
	}

	// Audience claim of JWT can be a string or an array of strings
	switch aud := claims.Audience.(type) {
	case string:
		if aud == audience {
			return nil
		}
	case []interface{}:
		for _, audItem := range aud {
			if audStr, ok := audItem.(string); ok && audStr == audience {
				return nil
			}
		}
	}

	return fmt.Errorf("invalid JWT token audience field")
}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Configure the IdP to include the audience: add an audience mapper/claim (Keycloak), set the API identifier as audience (Auth0), or expose an API/scope so the token carries the resource aud (Azure AD).
  2. Ensure the audience parameter NetBird sends in the authorization request matches an audience the IdP actually supports - the flow passes it via oauth2.SetAuthURLParam("audience", ...).
  3. Re-login after fixing the IdP so a freshly minted token is checked.
  4. If the provider truly cannot add aud, switch to a provider configuration that supports JWT audience validation.
Defensive patterns

Strategy: validation

Validate before calling

// assert a decoded token carries an aud claim before it reaches validation
func hasAudienceClaim(token string) bool {
    parts := strings.Split(token, ".")
    if len(parts) != 3 {
        return false
    }
    payload, err := base64.RawURLEncoding.DecodeString(parts[1])
    if err != nil {
        return false
    }
    var claims map[string]json.RawMessage
    return json.Unmarshal(payload, &claims) == nil && claims["aud"] != nil
}

Try / catch

if err := validateTokenAudience(token, audience); err != nil {
    if strings.Contains(err.Error(), "audience is absent") {
        // add an audience mapper / API identifier in the IdP; the flow must
        // re-issue a token, so re-login after the fix
    }
}

Prevention

When it happens

Trigger: The IdP mints tokens without an aud claim: the authorization request carried no audience/resource parameter, the API registration in the IdP has no identifier configured, or a custom claims policy strips aud.

Common situations: Keycloak client config with no audience mapper in the client scope; Auth0 API identifier not passed as the audience parameter; Azure AD app registration missing the API/exposed scope so tokens come out without the resource audience.

Related errors


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