{"record":{"id":"7c372aeb254bde80","repo":"wavetermdev/waveterm","slug":"cannot-auth-router-via-token","errorCode":null,"errorMessage":"cannot auth router via token","messagePattern":"cannot auth router via token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrouter_controlimpl.go","lineNumber":133,"sourceCode":"\t\tlog.Printf(\"wshrouter authenticate success linkid=%d routeid=%q\", linkId, routeId)\n\t\timpl.Router.trustLink(linkId, LinkKind_Leaf)\n\t\timpl.Router.bindRoute(linkId, routeId, true)\n\t}\n\n\treturn rtnData, nil\n}\n\nfunc extractTokenData(token string) (wshrpc.CommandAuthenticateRtnData, error) {\n\tentry := shellutil.GetAndRemoveTokenSwapEntry(token)\n\tif entry == nil {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no token entry found\")\n\t}\n\t_, err := validateRpcContextFromAuth(entry.RpcContext)\n\tif err != nil {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, err\n\t}\n\tif entry.RpcContext.IsRouter {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"cannot auth router via token\")\n\t}\n\trouteId := entry.RpcContext.GenerateRouteId()\n\tif routeId == \"\" {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"no routeid\")\n\t}\n\treturn wshrpc.CommandAuthenticateRtnData{\n\t\tRouteId:        routeId,\n\t\tEnv:            entry.Env,\n\t\tInitScriptText: entry.ScriptText,\n\t\tRpcContext:     entry.RpcContext,\n\t}, nil\n}\n\nfunc (impl *WshRouterControlImpl) AuthenticateTokenVerifyCommand(ctx context.Context, data wshrpc.CommandAuthenticateTokenData) (wshrpc.CommandAuthenticateRtnData, error) {\n\tif !impl.Router.IsRootRouter() {\n\t\treturn wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf(\"authenticatetokenverify can only be called on root router\")\n\t}\n\tif data.Token == \"\" {","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrouter_controlimpl.go#L115-L151","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Mint the connection token with a leaf (non-router) RpcContext: set IsRouter=false and supply a valid RouteId or ProcRoute.","If the peer really is a router, use the JWT-based AuthenticateCommand flow instead of AuthenticateTokenCommand.","Audit the token-minting code path that created the entry to confirm it targets leaf connections."],"exampleFix":"// before\nrpcCtx := &wshrpc.RpcContext{IsRouter: true}\ntoken := shellutil.MintConnectionToken(rpcCtx, ...) // rejected at verify\n// after\nrpcCtx := &wshrpc.RpcContext{IsRouter: false, RouteId: \"client-route-id\"}\ntoken := shellutil.MintConnectionToken(rpcCtx, ...)","handlingStrategy":"validation","validationCode":"// on the minting side, before creating the token\nif rpcCtx.IsRouter {\n    return fmt.Errorf(\"use JWT AuthenticateCommand for router contexts, not token-swap\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only mint token-swap entries for leaf (non-router) RpcContexts","Route router links through the JWT AuthenticateCommand flow","Audit custom minting scripts for IsRouter: true"],"tags":["auth","token","router","policy"],"backgroundTag":"router-auth-not-allowed-via-token","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}