repo_description: Scalable real-time messaging server in a language-agnostic way. Self-hosted alternative to Pubnub, Pusher, Ably, socket.io, Phoenix.PubSub, SignalR. Set up once and forever.
repo_topics: ably alternative eventsource grpc http-streaming http3 messaging pubnub pubsub pusher real-time redis scalability server-sent-events socket-io sse streaming websocket websockets webtransport
code: package cli
  
  import (
  	"encoding/json"
  	"fmt"
  	"os"
  
  	"github.com/centrifugal/centrifugo/v6/internal/config"
  	"github.com/centrifugal/centrifugo/v6/internal/confighelpers"
  	"github.com/centrifugal/centrifugo/v6/internal/jwtverify"
  
  	"github.com/cristalhq/jwt/v5"
  	"github.com/spf13/cobra"
  )
  
  func CheckToken() *cobra.Command {
  	var checkTokenConfigFile string
  	var checkTokenCmd = &cobra.Command{
  		Use:   "checktoken [TOKEN]",
  		Short: "Check connection JWT",
  		Long:  `Check connection JWT`,
  		Run: func(cmd *cobra.Command, args []string) {
  			checkToken(cmd, checkTokenConfigFile, args)
  		},
  	}
  	checkTokenCmd.Flags().StringVarP(&checkTokenConfigFile, "config", "c", "config.json", "path to config file")
  	return checkTokenCmd
  }
  
  func checkToken(cmd *cobra.Command, checkTokenConfigFile string, args []string) {
  	cfg, _, err := config.GetConfig(cmd, checkTokenConfigFile)
  	if err != nil {
  		fmt.Printf("error getting config: %v\n", err)
  		os.Exit(1)
  	}
  	verifierConfig, err := confighelpers.MakeVerifierConfig(cfg.Client.Token)
  	if err != nil {
  		fmt.Printf("error: %v\n", err)
  		os.Exit(1)
  	}
  	if len(args) != 1 {
  		fmt.Printf("error: provide token to check [centrifugo checktoken <TOKEN>]\n")
  		os.Exit(1)
  	}
  	subject, claims, err := parseAndVerifyToken(verifierConfig, cfg, args[0])
  	if err != nil {
  		fmt.Printf("error: %v\n", err)
  		os.Exit(1)
  	}
  	var user = fmt.Sprintf("user %s", subject)
  	if subject == "" {
  		user = "anonymous user"
  	}
  	fmt.Printf("valid token for %s\npayload: %s\n", user, string(claims))
  }
  
  // parseAndVerifyToken checks JWT for user.
  func parseAndVerifyToken(config jwtverify.VerifierConfig, ruleConfig config.Config, t string) (string, []byte, error) {
  	token, err := jwt.ParseNoVerify([]byte(t)) // Will be verified later.
  	if err != nil {
  		return "", nil, err
  	}
  
  	claims := &jwt.RegisteredClaims{}
  	err = json.Unmarshal(token.Claims(), claims)
  	if err != nil {
  		return "", nil, err
  	}
  
  	ct, err := verify(config, ruleConfig, t)
  	if err != nil {
  		return "", nil, fmt.Errorf("token with algorithm %s and claims %s has error: %v", token.Header().Algorithm, string(token.Claims()), err)
  	}
  
  	return ct.UserID, token.Claims(), nil
  }
  
  func verify(verifierConf jwtverify.VerifierConfig, cfg config.Config, token string) (jwtverify.ConnectToken, error) {
  	cfgContainer, err := config.NewContainer(cfg)
  	if err != nil {
  		return jwtverify.ConnectToken{}, err
  	}
  	verifier, err := jwtverify.NewTokenVerifierJWT(verifierConf, cfgContainer)
  	if err != nil {
  		return jwtverify.ConnectToken{}, err
  	}
  	return verifier.VerifyConnectToken(token, false)
  }
  