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 nil

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Set the exact same authentication.token string on frps and frpc (and any visitor configs)
  2. Check for invisible whitespace or quotes around the token in TOML/INI/YAML
  3. If implementing the protocol yourself, compute md5(token || strconv.FormatInt(timestamp,10)) as lowercase hex and send the same timestamp in the Login message
  4. 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

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


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