wavetermdev/waveterm · error

cannot auth router via token

Error message

cannot auth router via token

What it means

extractTokenData explicitly rejects token entries whose RpcContext has IsRouter=true. Token-swap authentication is only meant for leaf connections (terminals, clients); router-level links must authenticate with a signed JWT through AuthenticateCommand instead. This is a policy guard, not a validation failure.

Source

Thrown at pkg/wshutil/wshrouter_controlimpl.go:133

		log.Printf("wshrouter authenticate success linkid=%d routeid=%q", linkId, routeId)
		impl.Router.trustLink(linkId, LinkKind_Leaf)
		impl.Router.bindRoute(linkId, routeId, true)
	}

	return rtnData, nil
}

func extractTokenData(token string) (wshrpc.CommandAuthenticateRtnData, error) {
	entry := shellutil.GetAndRemoveTokenSwapEntry(token)
	if entry == nil {
		return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no token entry found")
	}
	_, err := validateRpcContextFromAuth(entry.RpcContext)
	if err != nil {
		return wshrpc.CommandAuthenticateRtnData{}, err
	}
	if entry.RpcContext.IsRouter {
		return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("cannot auth router via token")
	}
	routeId := entry.RpcContext.GenerateRouteId()
	if routeId == "" {
		return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no routeid")
	}
	return wshrpc.CommandAuthenticateRtnData{
		RouteId:        routeId,
		Env:            entry.Env,
		InitScriptText: entry.ScriptText,
		RpcContext:     entry.RpcContext,
	}, nil
}

func (impl *WshRouterControlImpl) AuthenticateTokenVerifyCommand(ctx context.Context, data wshrpc.CommandAuthenticateTokenData) (wshrpc.CommandAuthenticateRtnData, error) {
	if !impl.Router.IsRootRouter() {
		return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("authenticatetokenverify can only be called on root router")
	}
	if data.Token == "" {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Mint the connection token with a leaf (non-router) RpcContext: set IsRouter=false and supply a valid RouteId or ProcRoute.
  2. If the peer really is a router, use the JWT-based AuthenticateCommand flow instead of AuthenticateTokenCommand.
  3. Audit the token-minting code path that created the entry to confirm it targets leaf connections.

Example fix

// before
rpcCtx := &wshrpc.RpcContext{IsRouter: true}
token := shellutil.MintConnectionToken(rpcCtx, ...) // rejected at verify
// after
rpcCtx := &wshrpc.RpcContext{IsRouter: false, RouteId: "client-route-id"}
token := shellutil.MintConnectionToken(rpcCtx, ...)
Defensive patterns

Strategy: validation

Validate before calling

// on the minting side, before creating the token
if rpcCtx.IsRouter {
    return fmt.Errorf("use JWT AuthenticateCommand for router contexts, not token-swap")
}

Prevention

When it happens

Trigger: Calling AuthenticateTokenCommand / AuthenticateTokenVerifyCommand with a valid, unredeemed token whose stored RpcContext was created with IsRouter set — i.e. a token minted for a router entity rather than a leaf.

Common situations: Misconfigured tooling minting connection tokens for router contexts; a proxy/router node trying to bootstrap via token-swap instead of JWT auth; custom scripts copying the token minting code with IsRouter: true.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/7c372aeb254bde80. Report an issue: GitHub.