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

  1. Restart the frpc connection after changing OIDC credentials so Login and Ping are signed by the same identity
  2. Ensure exactly one frpc instance (one identity) uses a given frps session — do not share runIDs across instances
  3. Confirm the IdP issues a stable sub claim for the same client across refreshes
  4. 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

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


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