Skip to content

Commit

Permalink
Merge pull request #2 from GetStream/consitency-python-token
Browse files Browse the repository at this point in the history
[VID-103]: fix: align token generation with python sdk
  • Loading branch information
tbarbugli authored Oct 4, 2024
2 parents eb6de6d + 876b699 commit 695f5dd
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 29 deletions.
78 changes: 52 additions & 26 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func NewClient(apiKey, apiSecret string, options ...ClientOption) (*Client, erro
fn(client)
}

token, err := client.createToken(jwt.MapClaims{"server": true})
token, err := client.createTokenWithClaims(jwt.MapClaims{"server": true})
if err != nil {
return nil, err
}
Expand All @@ -131,58 +131,84 @@ func NewClient(apiKey, apiSecret string, options ...ClientOption) (*Client, erro
return client, nil
}

type StreamJWTClaims struct {
Expire *time.Time
IssuedAt *time.Time
ChannelCIDs []string
CallCIDs []string
Role string
// Claims contains optional parameters for token creation.
type Claims struct {
Role string // Role assigned to the user
ChannelCIDs []string // Channel IDs the user has access to
CallCIDs []string // Call IDs the user has access to
CustomClaims map[string]interface{} // Additional custom claims
}

func (c *Client) CreateTokenWithClaims(userID string, claims *StreamJWTClaims) (string, error) {
func (c *Client) createToken(userID string, claims *Claims, expiration int64) (string, error) {
if userID == "" {
return "", errors.New("user ID is empty")
return "", errors.New("user ID is required")
}

now := time.Now().Unix()
jwtClaims := jwt.MapClaims{
"user_id": userID,
"iat": now,
}

// Set issued at time; use the provided time or the current time if not provided.
if claims != nil {
if claims.IssuedAt != nil && !claims.IssuedAt.IsZero() {
jwtClaims["iat"] = claims.IssuedAt.Unix()
} else {
now := time.Now()
jwtClaims["iat"] = now.Unix()
// Set expiration time if provided
if expiration > 0 {
jwtClaims["exp"] = now + expiration
}

// Set expiration time if provided.
if claims.Expire != nil && !claims.Expire.IsZero() {
jwtClaims["exp"] = claims.Expire.Unix()
// Set role if provided
if claims.Role != "" {
jwtClaims["role"] = claims.Role
}

// Add channel IDs if provided.
// Set channel CIDs if provided
if len(claims.ChannelCIDs) > 0 {
jwtClaims["channel_cids"] = claims.ChannelCIDs
}

// Add call IDs if provided.
// Set call CIDs if provided
if len(claims.CallCIDs) > 0 {
jwtClaims["call_cids"] = claims.CallCIDs
}

// Add role if provided.
if claims.Role != "" {
jwtClaims["role"] = claims.Role
// Set custom claims if provided
if len(claims.CustomClaims) > 0 {
for key, value := range claims.CustomClaims {
jwtClaims[key] = value
}
}
}

return c.createToken(jwtClaims)
return c.createTokenWithClaims(jwtClaims)
}

func (c *Client) createCallToken(userID string, claims *Claims, expiration int64) (string, error) {
if userID == "" {
return "", errors.New("user ID is required")
}

// Ensure that CallCIDs are included for call tokens
if claims == nil {
claims = &Claims{}
}
if len(claims.CallCIDs) == 0 {
return "", errors.New("call_cids are required for call tokens")
}

return c.createToken(userID, claims, expiration)
}

func (c *Client) createToken(claims jwt.Claims) (string, error) {
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(c.apiSecret)
// createToken signs the JWT with the provided claims.
//
// Parameters:
// - claims (jwt.MapClaims): The claims to include in the token.
//
// Returns:
// - (string): The signed JWT token.
// - (error): An error object if signing fails.
func (c *Client) createTokenWithClaims(claims jwt.MapClaims) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(c.apiSecret)
}

// VerifyWebhook validates if hmac signature is correct for message body.
Expand Down
2 changes: 1 addition & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ func TestVideoExamples(t *testing.T) {
_, err := client.UpdateUsersPartial(ctx, &UpdateUsersPartialRequest{Users: users})
assert.NoError(t, err)

token, err := client.CreateToken("tommaso-id", nil)
token, err := client.CreateToken("tommaso-id", nil, 3600)
assert.NoError(t, err)
assert.NotEmpty(t, token)
})
Expand Down
47 changes: 45 additions & 2 deletions stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,51 @@ func New(apiKey, apiSecret string, options ...ClientOption) *Stream {
}
}

func (s *Stream) CreateToken(userID string, claims *StreamJWTClaims) (string, error) {
return s.CreateTokenWithClaims(userID, claims)
// CreateToken generates a token for a given user ID, with optional claims.
//
// Parameters:
// - userID (string): The unique identifier of the user for whom the token is being created.
// - claims (*Claims): A pointer to a Claims struct containing optional parameters.
//
// Returns:
// - (string): The generated JWT token.
// - (error): An error object if token creation fails.
//
// Example:
//
// expiration:= 3600, // Token expires in 1 hour
// claims := &Claims{
//
// Role: "admin",
// ChannelCIDs: []string{"channel1", "channel2"},
// }
//
// token, err := client.CreateToken("userID", claims, 3600)
func (s *Stream) CreateToken(userID string, claims *Claims, expiration int64) (string, error) {
return s.createToken(userID, claims, expiration)
}

// CreateCallToken generates a token for a given user ID, including optional claims specific to calls.
//
// Parameters:
// - userID (string): The unique identifier of the user for whom the token is being created.
// - claims (*Claims): A pointer to a Claims struct containing optional parameters.
//
// Returns:
// - (string): The generated JWT token.
// - (error): An error object if token creation fails.
//
// Example:
//
// claims := &Claims{
// Role: "moderator",
// CallCIDs: []string{"call1", "call2"},
// }
//
// expiration:= 7200, // Token expires in 2 hours
// token, err := client.CreateCallToken("userID", claims, expiration)
func (s *Stream) CreateCallToken(userID string, claims *Claims, expiration int64) (string, error) {
return s.createCallToken(userID, claims, expiration)
}

// Chat
Expand Down

0 comments on commit 695f5dd

Please sign in to comment.