{"record":{"id":"f0c23dd7ec2a69cd","repo":"fatedier/frp","slug":"invalid-oidc-token-in-ping-v","errorCode":null,"errorMessage":"invalid OIDC token in ping: %v","messagePattern":"invalid OIDC token in ping: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/auth/oidc.go","lineNumber":312,"sourceCode":"\t\tsubjectsFromLogin:    make(map[string]struct{}),\n\t}\n}\n\nfunc (auth *OidcAuthConsumer) VerifyLogin(loginMsg *msg.Login) (err error) {\n\ttoken, err := auth.verifier.Verify(context.Background(), loginMsg.PrivilegeKey)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid OIDC token in login: %v\", err)\n\t}\n\tauth.mu.Lock()\n\tauth.subjectsFromLogin[token.Subject] = struct{}{}\n\tauth.mu.Unlock()\n\treturn nil\n}\n\nfunc (auth *OidcAuthConsumer) verifyPostLoginToken(privilegeKey string) (err error) {\n\ttoken, err := auth.verifier.Verify(context.Background(), privilegeKey)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid OIDC token in ping: %v\", err)\n\t}\n\tauth.mu.RLock()\n\t_, ok := auth.subjectsFromLogin[token.Subject]\n\tauth.mu.RUnlock()\n\tif !ok {\n\t\treturn fmt.Errorf(\"received different OIDC subject in login and ping. \"+\n\t\t\t\"new subject: %s\",\n\t\t\ttoken.Subject)\n\t}\n\treturn nil\n}\n\nfunc (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {\n\tif !slices.Contains(auth.additionalAuthScopes, v1.AuthScopeHeartBeats) {\n\t\treturn nil\n\t}\n\n\treturn auth.verifyPostLoginToken(pingMsg.PrivilegeKey)","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/auth/oidc.go#L294-L330","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# frpc.toml before — stale cached token sent in pings\nauthentication.oidc.additionalAuthScopes = [\"HeartBeats\", \"NewWorkConns\"]\n\n# after — ensure fresh tokens by fixing token endpoint reachability\nauthentication.oidc.tokenEndpointURL = \"https://idp.example.com/realms/frp/protocol/openid-connect/token\"\nauthentication.oidc.additionalAuthScopes = [\"HeartBeats\", \"NewWorkConns\"]","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := consumer.VerifyPing(pingMsg); err != nil {\n    if isExpiredTokenErr(err) {\n        // force client re-auth: drop session so frpc performs a fresh Login\n        closeSession(); return err\n    }\n    return err\n}","preventionTips":["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"],"tags":["frp","oidc","jwt","heartbeat","verification"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}