fatedier/frp · error

cannot specify both auth.oidc.tokenSource and any other fiel

Error message

cannot specify both auth.oidc.tokenSource and any other field of auth.oidc

What it means

When auth.oidc.tokenSource is configured, it must be the ONLY field under auth.oidc. The validator rejects any config that sets tokenSource alongside clientID, clientSecret, audience, scope, tokenEndpointURL, additionalEndpointParams, trustedCaFile, insecureSkipVerify, or proxyURL, because the token then comes entirely from the external source.

Source

Thrown at pkg/config/v1/validation/client.go:130

	}
	if c.Method == v1.AuthMethodOIDC && c.OIDC.TokenSource == nil {
		if err := ValidateOIDCClientCredentialsConfig(&c.OIDC); err != nil {
			errs = AppendError(errs, err)
		}
	}
	return nil, errs
}

func (v *ConfigValidator) validateOIDCConfig(c *v1.AuthOIDCClientConfig) error {
	if c.TokenSource == nil {
		return nil
	}
	var errs error
	// Validate oidc.tokenSource mutual exclusivity with other fields of oidc
	if c.ClientID != "" || c.ClientSecret != "" || c.Audience != "" ||
		c.Scope != "" || c.TokenEndpointURL != "" || len(c.AdditionalEndpointParams) > 0 ||
		c.TrustedCaFile != "" || c.InsecureSkipVerify || c.ProxyURL != "" {
		errs = AppendError(errs, fmt.Errorf("cannot specify both auth.oidc.tokenSource and any other field of auth.oidc"))
	}
	if c.TokenSource.Type == "exec" {
		if err := v.ValidateUnsafeFeature(security.TokenSourceExec); err != nil {
			errs = AppendError(errs, err)
		}
	}
	if err := c.TokenSource.Validate(); err != nil {
		errs = AppendError(errs, fmt.Errorf("invalid auth.oidc.tokenSource: %v", err))
	}
	return errs
}

func validateTransportConfig(c *v1.ClientTransportConfig) (Warning, error) {
	var (
		warnings Warning
		errs     error
	)

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Delete all other auth.oidc fields (clientID, clientSecret, audience, scope, tokenEndpointURL, additionalEndpointParams, trustedCaFile, insecureSkipVerify, proxyURL) leaving only tokenSource
  2. Or drop tokenSource and use full static OIDC credentials instead
  3. Re-run frpc verify (frpc verify -c frpc.toml) after editing to confirm the exclusivity check passes

Example fix

# before
[auth]
method = "oidc"
[auth.oidc]
clientID = "my-client"
audience = "frp"
[auth.oidc.tokenSource]
type = "exec"
command = "oidc-token-helper"

# after
[auth]
method = "oidc"
[auth.oidc]
[auth.oidc.tokenSource]
type = "exec"
command = "oidc-token-helper"
Defensive patterns

Strategy: validation

Validate before calling

func oidcExclusive(c *v1.AuthOIDCClientConfig) bool {
    if c.TokenSource == nil {
        return true
    }
    return c.ClientID == "" && c.ClientSecret == "" && c.Audience == "" &&
        c.Scope == "" && c.TokenEndpointURL == "" &&
        len(c.AdditionalEndpointParams) == 0 && c.TrustedCaFile == "" &&
        !c.InsecureSkipVerify && c.ProxyURL == ""
}

Try / catch

if _, err := validation.ValidateClientCommonConfig(cfg); err != nil {
    if strings.Contains(err.Error(), "cannot specify both auth.oidc.tokenSource") {
        cfg.Auth.OIDC = v1.AuthOIDCClientConfig{TokenSource: cfg.Auth.OIDC.TokenSource}
    }
}

Prevention

When it happens

Trigger: Setting auth.oidc.tokenSource.* while leaving any static OIDC credential field populated, e.g. keeping clientID/clientSecret from a previously working static config after adding a tokenSource exec plugin.

Common situations: Gradually migrating from static OIDC client credentials to an exec-based token source and forgetting to delete the old fields; merging config snippets; defaults injected by a template tool that fill audience or scope automatically.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/91f73a8117339353. Report an issue: GitHub.