chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TokenClaims struct {
|
||||
Version int `json:"v"`
|
||||
RoomID string `json:"room_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ExpiresAt int64 `json:"exp"`
|
||||
}
|
||||
|
||||
type TokenIssuer interface {
|
||||
Issue(roomID, userID string, expiresAt time.Time) (string, error)
|
||||
}
|
||||
|
||||
type LocalTokenIssuer struct{ secret []byte }
|
||||
|
||||
func NewLocalTokenIssuer(secret []byte) (*LocalTokenIssuer, error) {
|
||||
if len(secret) < 32 {
|
||||
return nil, errors.New("RTC token signing secret must contain at least 32 bytes")
|
||||
}
|
||||
return &LocalTokenIssuer{secret: append([]byte(nil), secret...)}, nil
|
||||
}
|
||||
|
||||
func (i *LocalTokenIssuer) Issue(roomID, userID string, expiresAt time.Time) (string, error) {
|
||||
if roomID == "" || userID == "" || expiresAt.IsZero() {
|
||||
return "", errors.New("room, user and expiry are required")
|
||||
}
|
||||
payload, err := json.Marshal(TokenClaims{Version: 1, RoomID: roomID, UserID: userID, ExpiresAt: expiresAt.Unix()})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
encoded := base64.RawURLEncoding.EncodeToString(payload)
|
||||
return "dev1." + encoded + "." + base64.RawURLEncoding.EncodeToString(i.sign(encoded)), nil
|
||||
}
|
||||
|
||||
func (i *LocalTokenIssuer) Verify(token string, now time.Time) (TokenClaims, error) {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 || parts[0] != "dev1" {
|
||||
return TokenClaims{}, errors.New("invalid RTC token")
|
||||
}
|
||||
signature, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||
if err != nil || !hmac.Equal(signature, i.sign(parts[1])) {
|
||||
return TokenClaims{}, errors.New("invalid RTC token")
|
||||
}
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return TokenClaims{}, errors.New("invalid RTC token")
|
||||
}
|
||||
var claims TokenClaims
|
||||
if err := json.Unmarshal(payload, &claims); err != nil || claims.Version != 1 || claims.RoomID == "" || claims.UserID == "" {
|
||||
return TokenClaims{}, errors.New("invalid RTC token")
|
||||
}
|
||||
if now.Unix() >= claims.ExpiresAt {
|
||||
return TokenClaims{}, errors.New("RTC token expired")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (i *LocalTokenIssuer) sign(payload string) []byte {
|
||||
mac := hmac.New(sha256.New, i.secret)
|
||||
_, _ = mac.Write([]byte(payload))
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
Reference in New Issue
Block a user