snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
123
internal/cli/gensubtoken.go
Normal file
123
internal/cli/gensubtoken.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
func GenSubToken() *cobra.Command {
|
||||
var genSubTokenConfigFile string
|
||||
var genSubTokenUser string
|
||||
var genSubTokenChannel string
|
||||
var genSubTokenTTL int64
|
||||
var genSubTokenQuiet bool
|
||||
var genSubTokenCmd = &cobra.Command{
|
||||
Use: "gensubtoken",
|
||||
Short: "Generate sample subscription JWT for user",
|
||||
Long: `Generate sample subscription JWT for user`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genSubToken(cmd, genSubTokenConfigFile, genSubTokenUser, genSubTokenChannel, genSubTokenTTL, genSubTokenQuiet)
|
||||
},
|
||||
}
|
||||
genSubTokenCmd.Flags().StringVarP(&genSubTokenConfigFile, "config", "c", "config.json", "path to config file")
|
||||
genSubTokenCmd.Flags().StringVarP(&genSubTokenUser, "user", "u", "", "user ID")
|
||||
genSubTokenCmd.Flags().StringVarP(&genSubTokenChannel, "channel", "s", "", "channel")
|
||||
genSubTokenCmd.Flags().Int64VarP(&genSubTokenTTL, "ttl", "t", 3600*24*7, "token TTL in seconds, use -1 for token without expiration")
|
||||
genSubTokenCmd.Flags().BoolVarP(&genSubTokenQuiet, "quiet", "q", false, "only output the token without anything else")
|
||||
return genSubTokenCmd
|
||||
}
|
||||
|
||||
func genSubToken(
|
||||
cmd *cobra.Command, genSubTokenConfigFile string, genSubTokenUser string,
|
||||
genSubTokenChannel string, genSubTokenTTL int64, genSubTokenQuiet bool,
|
||||
) {
|
||||
cfg, _, err := config.GetConfig(cmd, genSubTokenConfigFile)
|
||||
if err != nil {
|
||||
fmt.Printf("error getting config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if genSubTokenChannel == "" {
|
||||
fmt.Println("channel is required")
|
||||
os.Exit(1)
|
||||
}
|
||||
verifierConfig, err := confighelpers.MakeVerifierConfig(cfg.Client.Token)
|
||||
if err != nil {
|
||||
fmt.Printf("error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if cfg.Client.SubscriptionToken.Enabled {
|
||||
verifierConfig, err = confighelpers.MakeVerifierConfig(cfg.Client.SubscriptionToken.Token)
|
||||
if err != nil {
|
||||
fmt.Printf("error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
token, err := generateSubToken(verifierConfig, genSubTokenUser, genSubTokenChannel, genSubTokenTTL)
|
||||
if err != nil {
|
||||
fmt.Printf("error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var user = fmt.Sprintf("user \"%s\"", genSubTokenUser)
|
||||
if genSubTokenUser == "" {
|
||||
user = "anonymous user"
|
||||
}
|
||||
exp := "without expiration"
|
||||
if genSubTokenTTL >= 0 {
|
||||
exp = fmt.Sprintf("with expiration TTL %s", time.Duration(genSubTokenTTL)*time.Second)
|
||||
}
|
||||
if genSubTokenQuiet {
|
||||
fmt.Print(token)
|
||||
return
|
||||
}
|
||||
fmt.Printf("HMAC SHA-256 JWT for %s and channel \"%s\" %s:\n%s\n", user, genSubTokenChannel, exp, token)
|
||||
}
|
||||
|
||||
// generateSubToken generates sample subscription JWT for user.
|
||||
func generateSubToken(config jwtverify.VerifierConfig, user string, channel string, ttlSeconds int64) (string, error) {
|
||||
if config.HMACSecretKey == "" {
|
||||
return "", errors.New("gensubtoken only supports generating HMAC-signed tokens, but no HMAC secret key is set in the configuration (client.subscription_token.token.hmac_secret_key, or client.token.hmac_secret_key when subscription token is not separately configured) – if you use RSA/ECDSA/JWKS token verification you need to generate the JWT with your own tooling")
|
||||
}
|
||||
signer, err := jwt.NewSignerHS(jwt.HS256, []byte(config.HMACSecretKey))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating HMAC signer: %w", err)
|
||||
}
|
||||
builder := jwt.NewBuilder(signer)
|
||||
claims := jwtverify.SubscribeTokenClaims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
Subject: user,
|
||||
},
|
||||
Channel: channel,
|
||||
}
|
||||
if ttlSeconds > 0 {
|
||||
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Duration(ttlSeconds) * time.Second))
|
||||
}
|
||||
|
||||
encodedClaims, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if config.UserIDClaim != "" {
|
||||
encodedClaims, err = sjson.SetBytes(encodedClaims, config.UserIDClaim, user)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
token, err := builder.Build(encodedClaims)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token.String(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user