Skip to content

Commit

Permalink
Option to configure custom token user id claim (#783)
Browse files Browse the repository at this point in the history
  • Loading branch information
FZambia authored Sep 2, 2024
1 parent b23476d commit bfee340
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 66 deletions.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ require (
github.com/rs/zerolog v1.33.0
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
github.com/tidwall/gjson v1.17.3
github.com/tidwall/sjson v1.2.5
github.com/twmb/franz-go v1.17.1
github.com/twmb/franz-go/pkg/kadm v1.13.0
github.com/twmb/franz-go/pkg/kmsg v1.8.0
Expand Down Expand Up @@ -57,6 +59,8 @@ require (
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
go.uber.org/mock v0.4.0 // indirect
)

Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/twmb/franz-go v1.17.1 h1:0LwPsbbJeJ9R91DPUHSEd4su82WJWcTY1Zzbgbg4CeQ=
github.com/twmb/franz-go v1.17.1/go.mod h1:NreRdJ2F7dziDY/m6VyspWd6sNxHKXdMZI42UfQ3GXM=
github.com/twmb/franz-go/pkg/kadm v1.13.0 h1:bJq4C2ZikUE2jh/wl9MtMTQ/kpmnBgVFh8XMQBEC+60=
Expand Down
51 changes: 38 additions & 13 deletions internal/cli/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/centrifugal/centrifugo/v5/internal/jwtverify"
"github.com/centrifugal/centrifugo/v5/internal/rule"
"github.com/cristalhq/jwt/v5"
"github.com/tidwall/sjson"
)

// GenerateToken generates sample JWT for user.
Expand All @@ -21,14 +22,28 @@ func GenerateToken(config jwtverify.VerifierConfig, user string, ttlSeconds int6
return "", fmt.Errorf("error creating HMAC signer: %w", err)
}
builder := jwt.NewBuilder(signer)
claims := jwt.RegisteredClaims{
Subject: user,
IssuedAt: jwt.NewNumericDate(time.Now()),
claims := jwtverify.ConnectTokenClaims{
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now()),
Subject: user,
},
}
if ttlSeconds > 0 {
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Duration(ttlSeconds) * time.Second))
}
token, err := builder.Build(claims)

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
}
Expand All @@ -45,19 +60,29 @@ func GenerateSubToken(config jwtverify.VerifierConfig, user string, channel stri
return "", fmt.Errorf("error creating HMAC signer: %w", err)
}
builder := jwt.NewBuilder(signer)
claims := jwt.RegisteredClaims{
Subject: user,
IssuedAt: jwt.NewNumericDate(time.Now()),
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))
}
token, err := builder.Build(
jwtverify.SubscribeTokenClaims{
RegisteredClaims: claims,
Channel: channel,
},
)

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
}
Expand Down
23 changes: 20 additions & 3 deletions internal/jwtverify/token_verifier_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/cristalhq/jwt/v5"
"github.com/rakutentech/jwk-go/okp"
"github.com/rs/zerolog/log"
"github.com/tidwall/gjson"
)

type VerifierConfig struct {
Expand Down Expand Up @@ -56,6 +57,10 @@ type VerifierConfig struct {
// IssuerRegex allows setting Issuer in form of Go language regex pattern. Regex groups
// may be then used in constructing JWKSPublicEndpoint.
IssuerRegex string

// UserIDClaim allows overriding default claim used to extract user ID from token.
// By default, Centrifugo uses "sub" and we recommend keeping the default if possible.
UserIDClaim string
}

func (c VerifierConfig) Validate() error {
Expand Down Expand Up @@ -94,6 +99,7 @@ func NewTokenVerifierJWT(config VerifierConfig, ruleContainer *rule.Container) (
issuerRe: issuerRe,
audience: config.Audience,
audienceRe: audienceRe,
userIDClaim: config.UserIDClaim,
}

algorithms, err := newAlgorithms(config.HMACSecretKey, config.RSAPublicKey, config.ECDSAPublicKey)
Expand Down Expand Up @@ -122,6 +128,7 @@ type VerifierJWT struct {
audienceRe *regexp.Regexp
issuer string
issuerRe *regexp.Regexp
userIDClaim string
}

var (
Expand Down Expand Up @@ -582,13 +589,17 @@ func (verifier *VerifierJWT) VerifyConnectToken(t string, skipVerify bool) (Conn
}

ct := ConnectToken{
UserID: claims.RegisteredClaims.Subject,
Info: info,
Subs: subs,
ExpireAt: expireAt,
Meta: claims.Meta,
}

if verifier.userIDClaim != "" {
value := gjson.GetBytes(token.Claims(), verifier.userIDClaim)
ct.UserID = value.String()
} else {
ct.UserID = claims.RegisteredClaims.Subject
}
return ct, nil
}

Expand Down Expand Up @@ -742,6 +753,12 @@ func (verifier *VerifierJWT) VerifySubscribeToken(t string, skipVerify bool) (Su
Data: data,
},
}
if verifier.userIDClaim != "" {
value := gjson.GetBytes(token.Claims(), verifier.userIDClaim)
st.UserID = value.String()
} else {
st.UserID = claims.RegisteredClaims.Subject
}
return st, nil
}

Expand Down Expand Up @@ -772,11 +789,11 @@ func (verifier *VerifierJWT) Reload(config VerifierConfig) error {
return fmt.Errorf("error compiling issuer regex: %w", err)
}
}

verifier.algorithms = alg
verifier.audience = config.Audience
verifier.audienceRe = audienceRe
verifier.issuer = config.Issuer
verifier.issuerRe = issuerRe
verifier.userIDClaim = config.UserIDClaim
return nil
}
Loading

0 comments on commit bfee340

Please sign in to comment.