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
- Confirm both sides list the same additionalAuthScopes — frps expects HeartBeats only if frpc sends authenticated pings
- 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)
- Verify issuer/audience on frps match the token claims
- 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
- Keep frpc's token source healthy so pings carry fresh tokens (see error 142)
- Mirror additionalAuthScopes on both sides
- Drop sessions on expiry instead of resending the same stale JWT
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
- invalid OIDC token in login: %v
- failed to parse OIDC proxy URL %q: %w
- failed to create OIDC HTTP client: %w
- couldn't generate OIDC token for login: %v
- couldn't acquire OIDC token for login: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/f0c23dd7ec2a69cd.
Report an issue: GitHub.