fatedier/frp · error

invalid OIDC token in ping: %v

Error message

invalid OIDC token in ping: %v

What it means

Same verification path as error 144 but applied to heartbeat pings, only when 'HeartBeats' is listed in the additional auth scopes (oidc.additionalAuthScopes). VerifyPing → verifyPostLoginToken re-verifies pingMsg.PrivilegeKey with the TokenVerifier; any JWT validation failure (signature, expiry, issuer, audience) produces this error. After verification the subject is also cross-checked against the login subjects (error 146).

Source

Thrown at pkg/auth/oidc.go:312

		subjectsFromLogin:    make(map[string]struct{}),
	}
}

func (auth *OidcAuthConsumer) VerifyLogin(loginMsg *msg.Login) (err error) {
	token, err := auth.verifier.Verify(context.Background(), loginMsg.PrivilegeKey)
	if err != nil {
		return fmt.Errorf("invalid OIDC token in login: %v", err)
	}
	auth.mu.Lock()
	auth.subjectsFromLogin[token.Subject] = struct{}{}
	auth.mu.Unlock()
	return nil
}

func (auth *OidcAuthConsumer) verifyPostLoginToken(privilegeKey string) (err error) {
	token, err := auth.verifier.Verify(context.Background(), privilegeKey)
	if err != nil {
		return fmt.Errorf("invalid OIDC token in ping: %v", err)
	}
	auth.mu.RLock()
	_, ok := auth.subjectsFromLogin[token.Subject]
	auth.mu.RUnlock()
	if !ok {
		return fmt.Errorf("received different OIDC subject in login and ping. "+
			"new subject: %s",
			token.Subject)
	}
	return nil
}

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

	return auth.verifyPostLoginToken(pingMsg.PrivilegeKey)

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Confirm both sides list the same additionalAuthScopes — frps expects HeartBeats only if frpc sends authenticated pings
  2. Decode the ping JWT and check exp; if expired, investigate why frpc is not refreshing (see error 142 fixes: network/credentials to the token endpoint)
  3. Verify issuer/audience on frps match the token claims
  4. Reduce reliance on long-lived tokens, or remove HeartBeats from additionalAuthScopes if ping re-auth is not required

Example fix

# frpc.toml before — stale cached token sent in pings
authentication.oidc.additionalAuthScopes = ["HeartBeats", "NewWorkConns"]

# after — ensure fresh tokens by fixing token endpoint reachability
authentication.oidc.tokenEndpointURL = "https://idp.example.com/realms/frp/protocol/openid-connect/token"
authentication.oidc.additionalAuthScopes = ["HeartBeats", "NewWorkConns"]
Defensive patterns

Strategy: retry

Try / catch

if err := consumer.VerifyPing(pingMsg); err != nil {
    if isExpiredTokenErr(err) {
        // force client re-auth: drop session so frpc performs a fresh Login
        closeSession(); return err
    }
    return err
}

Prevention

When it happens

Trigger: frpc configured with HeartBeats in authentication.oidc.additionalAuthScopes sends Ping messages whose PrivilegeKey JWT fails frps verification: token expired mid-session (refresh not working on frpc), frpc restarted with a stale token, or issuer/signing-key changes on the IdP.

Common situations: The frpc token source cached a token that expired before the next ping; frpc's non-caching fallback lost access to the IdP so pings carry an old token; frps's verifier configured against a different realm than the token's issuer.

Related errors


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