fatedier/frp · error
received different OIDC subject in login and ping. new subje
Error message
received different OIDC subject in login and ping. new subject: %s
What it means
Anti-spoofing check on the frps side: after a ping's JWT verifies successfully, verifyPostLoginToken looks up the token's Subject claim in the set of subjects recorded during VerifyLogin. If the ping token's subject is not among them, frps concludes a different identity is now signing pings for a session established by another identity, and rejects it.
Source
Thrown at pkg/auth/oidc.go:318
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)
}
func (auth *OidcAuthConsumer) VerifyNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
if !slices.Contains(auth.additionalAuthScopes, v1.AuthScopeNewWorkConns) {
return nil
}View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Restart the frpc connection after changing OIDC credentials so Login and Ping are signed by the same identity
- Ensure exactly one frpc instance (one identity) uses a given frps session — do not share runIDs across instances
- Confirm the IdP issues a stable sub claim for the same client across refreshes
- If the migration to a new subject was intentional, re-login instead of reusing the existing session
Defensive patterns
Strategy: validation
Validate before calling
// before sending ping: ensure same subject as the login token
loginSub := subjectOf(loginToken)
pingSub := subjectOf(pingToken)
if loginSub != pingSub {
return errors.New("ping token subject differs from login; re-login required")
} Try / catch
if err := consumer.VerifyPing(pingMsg); err != nil && strings.Contains(err.Error(), "different OIDC subject") {
terminateSession() // identity change mid-session is unrecoverable; force fresh login
} Prevention
- Never change OIDC client credentials on a live frpc; restart instead
- One frpc identity per frps session
- Verify IdP issues stable sub claims across token refreshes
When it happens
Trigger: HeartBeats auth scope enabled, and the JWT attached to Ping resolves to a subject different from any subject seen in the Login message — e.g. frpc switched client credentials mid-session, the IdP merged/renamed the user, or one process reused a session with another principal's token.
Common situations: Rotating oidc.clientid/clientsecret (different service account) while a connection is alive; a load balancer putting pings from a second frpc instance onto the frps session of the first; tokens minted by a different realm after an issuer migration.
Related errors
- 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
- invalid OIDC token in login: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/def12e8694206d2f.
Report an issue: GitHub.