fatedier/frp · error

couldn't generate OIDC token for login: %v

Error message

couldn't generate OIDC token for login: %v

What it means

This is the client side of OIDC login. Before sending the Login message, OidcAuthProvider.SetLogin calls generateAccessToken, which asks the oauth2 client-credentials TokenSource for a token (via oidcTokenSource, possibly falling back to a non-caching source when the provider omits expires_in). Any failure of that token request — network error, bad client credentials, wrong token endpoint, or failing to decode the token response — surfaces here.

Source

Thrown at pkg/auth/oidc.go:191

	// source.  This avoids an eager network call at construction time, which
	// would prevent loopLoginUntilSuccess from retrying on transient IdP
	// outages.
	cachingSource := tokenGenerator.TokenSource(ctx)

	return &OidcAuthProvider{
		additionalAuthScopes: additionalAuthScopes,
		tokenSource: &oidcTokenSource{
			source:      cachingSource,
			fallbackCfg: tokenGenerator,
			fallbackCtx: ctx,
		},
	}, nil
}

func (auth *OidcAuthProvider) generateAccessToken() (accessToken string, err error) {
	tokenObj, err := auth.tokenSource.Token()
	if err != nil {
		return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
	}
	return tokenObj.AccessToken, nil
}

func (auth *OidcAuthProvider) SetLogin(loginMsg *msg.Login) (err error) {
	loginMsg.PrivilegeKey, err = auth.generateAccessToken()
	return err
}

func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) {
	if !slices.Contains(auth.additionalAuthScopes, v1.AuthScopeHeartBeats) {
		return nil
	}

	pingMsg.PrivilegeKey, err = auth.generateAccessToken()
	return err
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Read the wrapped %v — oauth2 errors include the HTTP status and response body from the IdP
  2. Verify oidc.tokenEndpointURL, oidc.clientid, oidc.clientsecret in the frpc config against the IdP
  3. Reproduce the request manually: curl -d 'grant_type=client_credentials&client_id=...&client_secret=...' <tokenEndpointURL>
  4. If the IdP uses a private CA, set oidc.trustedCaFile (or temporarily test with oidc.insecureSkipVerify)
  5. Check network/DNS/firewall reachability of the IdP from the frpc host

Example fix

# before
authentication.method = "oidc"
authentication.oidc.tokenEndpointURL = "https://idp.example.com/token"

# after (correct issuer path + credentials + CA)
authentication.method = "oidc"
authentication.oidc.tokenEndpointURL = "https://idp.example.com/realms/frp/protocol/openid-connect/token"
authentication.oidc.clientid = "frp-client"
authentication.oidc.clientsecret = "***"
authentication.oidc.trustedCaFile = "/etc/frp/idp-ca.pem"
Defensive patterns

Strategy: retry

Try / catch

token, err := auth.generateAccessToken()
if err != nil {
    if isRetryableTokenErr(err) { // network/5xx/timeouts
        token, err = backoff.Retry(auth.generateAccessToken)
    }
    if err != nil { return fmt.Errorf("login token fetch failed: %w", err) }
}

Prevention

When it happens

Trigger: frpc with authentication.method=oidc calling SetLogin: the POST to oidc.tokenEndpointURL with client_id/client_secret fails DNS/TLS/connectivity, returns 401 (invalid client credentials), returns a non-token body, or the provider's token endpoint URL is misconfigured.

Common situations: Wrong oidc.clientid/clientsecret pair; token endpoint URL missing or mistyped; the IdP is internal and unreachable from the frpc host; the IdP's TLS cert is not trusted (no trustedCaFile set); clock skew on the client.

Related errors


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