fatedier/frp · error
token in login doesn't match token from configuration
Error message
token in login doesn't match token from configuration
What it means
Static-token authentication check on frps. The expected key is md5hex(auth.token + decimal-timestamp) computed from the timestamp carried in the Login message, and compared with loginMsg.PrivilegeKey in constant time (util.ConstantTimeEqString). A mismatch means the frpc auth token differs from frps's, or the message was altered.
Source
Thrown at pkg/auth/token.go:66
pingMsg.Timestamp = time.Now().Unix()
pingMsg.PrivilegeKey = util.GetAuthKey(auth.token, pingMsg.Timestamp)
return nil
}
func (auth *TokenAuthSetterVerifier) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) error {
if !slices.Contains(auth.additionalAuthScopes, v1.AuthScopeNewWorkConns) {
return nil
}
newWorkConnMsg.Timestamp = time.Now().Unix()
newWorkConnMsg.PrivilegeKey = util.GetAuthKey(auth.token, newWorkConnMsg.Timestamp)
return nil
}
func (auth *TokenAuthSetterVerifier) VerifyLogin(m *msg.Login) error {
if !util.ConstantTimeEqString(util.GetAuthKey(auth.token, m.Timestamp), m.PrivilegeKey) {
return fmt.Errorf("token in login doesn't match token from configuration")
}
return nil
}
func (auth *TokenAuthSetterVerifier) VerifyPing(m *msg.Ping) error {
if !slices.Contains(auth.additionalAuthScopes, v1.AuthScopeHeartBeats) {
return nil
}
if !util.ConstantTimeEqString(util.GetAuthKey(auth.token, m.Timestamp), m.PrivilegeKey) {
return fmt.Errorf("token in heartbeat doesn't match token from configuration")
}
return nil
}
func (auth *TokenAuthSetterVerifier) VerifyNewWorkConn(m *msg.NewWorkConn) error {
if !slices.Contains(auth.additionalAuthScopes, v1.AuthScopeNewWorkConns) {
return nilView on GitHub (pinned to 6c8a8d0a97)
Solutions
- Set the exact same authentication.token string on frps and frpc (and any visitor configs)
- Check for invisible whitespace or quotes around the token in TOML/INI/YAML
- If implementing the protocol yourself, compute md5(token || strconv.FormatInt(timestamp,10)) as lowercase hex and send the same timestamp in the Login message
- Re-verify after editing both configs with a full frpc restart
Example fix
# frps.toml authentication.method = "token" authentication.token = "my-secret" # frpc.toml before authentication.method = "token" authentication.token = "my-Secret" # case/typo mismatch # frpc.toml after authentication.method = "token" authentication.token = "my-secret"
Defensive patterns
Strategy: validation
Validate before calling
if frpsToken != frpcToken {
return errors.New("auth.token mismatch between frps and frpc configs")
}
expected := md5hex(frpsToken + strconv.FormatInt(loginMsg.Timestamp, 10))
if expected != loginMsg.PrivilegeKey { return errors.New("login key mismatch") } Prevention
- Manage auth.token in a single shared secret (vault/K8s secret) consumed by both sides
- Diff the token values with 'cmp <(grep token frps.toml) <(grep token frpc.toml)' during setup
- Avoid trailing whitespace/quotes when editing configs
When it happens
Trigger: frps has authentication.token = A and frpc sends md5(B + m.Timestamp) because its token is B; or a hand-rolled client computed the key over the wrong fields (the hash is token concatenated with the Unix timestamp as a decimal string, in that order).
Common situations: auth.token set on one side only (other side defaults to empty or a different default); trailing whitespace/quotes around the token in one config; copying configs between environments without updating the token; custom SDK clients hashing token and timestamp in the wrong order or using hex of the timestamp.
Related errors
- token in heartbeat doesn't match token from configuration
- token in NewWorkConn doesn't match token from configuration
- couldn't generate OIDC token for login: %v
- invalid OIDC token in login: %v
- xtcp connection of [%s] auth failed
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/bd8b192458c6a9df.
Report an issue: GitHub.