package meet import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "time" ) type Config struct { AppID string AppSecret string Domain string } type RoomToken struct { Token string `json:"token"` Room string `json:"room"` Domain string `json:"domain"` MeetURL string `json:"meet_url"` } type UserInfo struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` Avatar string `json:"avatar,omitempty"` IsMod bool `json:"is_moderator"` } func NewConfig(appID, appSecret, domain string) *Config { return &Config{ AppID: appID, AppSecret: appSecret, Domain: domain, } } func (c *Config) GenerateToken(room string, user *UserInfo, ttl time.Duration) (*RoomToken, error) { now := time.Now() exp := now.Add(ttl) header := map[string]string{ "alg": "HS256", "typ": "JWT", } payload := map[string]any{ "iss": c.AppID, "sub": "meet.jitsi", "aud": "ulti", "iat": now.Unix(), "exp": exp.Unix(), "room": room, "context": map[string]any{ "user": map[string]any{ "id": user.ID, "name": user.Name, "email": user.Email, "avatar": user.Avatar, "moderator": user.IsMod, }, }, } token, err := signJWT(header, payload, c.AppSecret) if err != nil { return nil, err } return &RoomToken{ Token: token, Room: room, Domain: "meet.jitsi", MeetURL: fmt.Sprintf("https://%s/meet/%s?jwt=%s", c.Domain, room, token), }, nil } func signJWT(header map[string]string, payload map[string]any, secret string) (string, error) { headerJSON, err := json.Marshal(header) if err != nil { return "", err } payloadJSON, err := json.Marshal(payload) if err != nil { return "", err } headerB64 := base64URLEncode(headerJSON) payloadB64 := base64URLEncode(payloadJSON) signingInput := headerB64 + "." + payloadB64 mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(signingInput)) signature := base64URLEncode(mac.Sum(nil)) return signingInput + "." + signature, nil } func base64URLEncode(data []byte) string { return base64.RawURLEncoding.EncodeToString(data) }