{"record":{"id":"91f73a8117339353","repo":"fatedier/frp","slug":"cannot-specify-both-auth-oidc-tokensource-and-any","errorCode":null,"errorMessage":"cannot specify both auth.oidc.tokenSource and any other field of auth.oidc","messagePattern":"cannot specify both auth\\.oidc\\.tokenSource and any other field of auth\\.oidc","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/validation/client.go","lineNumber":130,"sourceCode":"\t}\n\tif c.Method == v1.AuthMethodOIDC && c.OIDC.TokenSource == nil {\n\t\tif err := ValidateOIDCClientCredentialsConfig(&c.OIDC); err != nil {\n\t\t\terrs = AppendError(errs, err)\n\t\t}\n\t}\n\treturn nil, errs\n}\n\nfunc (v *ConfigValidator) validateOIDCConfig(c *v1.AuthOIDCClientConfig) error {\n\tif c.TokenSource == nil {\n\t\treturn nil\n\t}\n\tvar errs error\n\t// Validate oidc.tokenSource mutual exclusivity with other fields of oidc\n\tif c.ClientID != \"\" || c.ClientSecret != \"\" || c.Audience != \"\" ||\n\t\tc.Scope != \"\" || c.TokenEndpointURL != \"\" || len(c.AdditionalEndpointParams) > 0 ||\n\t\tc.TrustedCaFile != \"\" || c.InsecureSkipVerify || c.ProxyURL != \"\" {\n\t\terrs = AppendError(errs, fmt.Errorf(\"cannot specify both auth.oidc.tokenSource and any other field of auth.oidc\"))\n\t}\n\tif c.TokenSource.Type == \"exec\" {\n\t\tif err := v.ValidateUnsafeFeature(security.TokenSourceExec); err != nil {\n\t\t\terrs = AppendError(errs, err)\n\t\t}\n\t}\n\tif err := c.TokenSource.Validate(); err != nil {\n\t\terrs = AppendError(errs, fmt.Errorf(\"invalid auth.oidc.tokenSource: %v\", err))\n\t}\n\treturn errs\n}\n\nfunc validateTransportConfig(c *v1.ClientTransportConfig) (Warning, error) {\n\tvar (\n\t\twarnings Warning\n\t\terrs     error\n\t)\n","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/validation/client.go#L112-L148","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete all other auth.oidc fields (clientID, clientSecret, audience, scope, tokenEndpointURL, additionalEndpointParams, trustedCaFile, insecureSkipVerify, proxyURL) leaving only tokenSource","Or drop tokenSource and use full static OIDC credentials instead","Re-run frpc verify (frpc verify -c frpc.toml) after editing to confirm the exclusivity check passes"],"exampleFix":"# before\n[auth]\nmethod = \"oidc\"\n[auth.oidc]\nclientID = \"my-client\"\naudience = \"frp\"\n[auth.oidc.tokenSource]\ntype = \"exec\"\ncommand = \"oidc-token-helper\"\n\n# after\n[auth]\nmethod = \"oidc\"\n[auth.oidc]\n[auth.oidc.tokenSource]\ntype = \"exec\"\ncommand = \"oidc-token-helper\"","handlingStrategy":"validation","validationCode":"func oidcExclusive(c *v1.AuthOIDCClientConfig) bool {\n    if c.TokenSource == nil {\n        return true\n    }\n    return c.ClientID == \"\" && c.ClientSecret == \"\" && c.Audience == \"\" &&\n        c.Scope == \"\" && c.TokenEndpointURL == \"\" &&\n        len(c.AdditionalEndpointParams) == 0 && c.TrustedCaFile == \"\" &&\n        !c.InsecureSkipVerify && c.ProxyURL == \"\"\n}","typeGuard":null,"tryCatchPattern":"if _, err := validation.ValidateClientCommonConfig(cfg); err != nil {\n    if strings.Contains(err.Error(), \"cannot specify both auth.oidc.tokenSource\") {\n        cfg.Auth.OIDC = v1.AuthOIDCClientConfig{TokenSource: cfg.Auth.OIDC.TokenSource}\n    }\n}","preventionTips":["When adopting tokenSource, zero out the whole oidc block first","Keep one canonical OIDC config per auth mode in your config management"],"tags":["frp","config","oidc","auth","mutual-exclusion","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}