package rtc import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/binary" "strings" "testing" "time" ) func TestVolcengineTokenIssuerProducesSignedScopedDeterministicToken(t *testing.T) { const appID = "661e7ae5543cf00000000000" appKey := "unit-test-" + strings.Repeat("x", 23) issuer, err := NewVolcengineTokenIssuer(appID, appKey, 5*time.Minute) if err != nil { t.Fatal(err) } expiresAt := time.Unix(1_800_000_000, 0).UTC() first, err := issuer.Issue("room-1", "device-1", expiresAt) if err != nil { t.Fatal(err) } second, err := issuer.Issue("room-1", "device-1", expiresAt) if err != nil || second != first { t.Fatalf("idempotent issue mismatch: err=%v equal=%v", err, second == first) } if len(first) <= 27 || first[:27] != "001"+appID { t.Fatalf("unexpected token prefix: %q", first) } content, err := base64.StdEncoding.DecodeString(first[27:]) if err != nil { t.Fatal(err) } message, rest := readTokenBytes(t, content) signature, rest := readTokenBytes(t, rest) if len(rest) != 0 { t.Fatalf("unexpected trailing token bytes: %d", len(rest)) } mac := hmac.New(sha256.New, []byte(appKey)) _, _ = mac.Write(message) if !hmac.Equal(signature, mac.Sum(nil)) { t.Fatal("token signature is invalid") } offset := 4 // deterministic nonce issuedAt := binary.LittleEndian.Uint32(message[offset : offset+4]) offset += 4 expires := binary.LittleEndian.Uint32(message[offset : offset+4]) offset += 4 room, remaining := readTokenBytes(t, message[offset:]) user, remaining := readTokenBytes(t, remaining) if string(room) != "room-1" || string(user) != "device-1" || issuedAt != uint32(expiresAt.Add(-5*time.Minute).Unix()) || expires != uint32(expiresAt.Unix()) { t.Fatalf("unexpected token scope room=%s user=%s issued=%d expires=%d", room, user, issuedAt, expires) } if len(remaining) != 2+5*6 || binary.LittleEndian.Uint16(remaining[:2]) != 5 { t.Fatalf("unexpected privileges payload: %x", remaining) } for index, want := range []uint16{0, 1, 2, 3, 4} { position := 2 + index*6 if got := binary.LittleEndian.Uint16(remaining[position : position+2]); got != want { t.Fatalf("privilege[%d]=%d want=%d", index, got, want) } if got := binary.LittleEndian.Uint32(remaining[position+2 : position+6]); got != uint32(expiresAt.Unix()) { t.Fatalf("privilege[%d] expiry=%d", index, got) } } } func TestVolcengineTokenIssuerValidatesApplicationIdentity(t *testing.T) { if _, err := NewVolcengineTokenIssuer("short", "0123456789abcdef", time.Minute); err == nil { t.Fatal("expected invalid app ID to be rejected") } } func readTokenBytes(t *testing.T, input []byte) ([]byte, []byte) { t.Helper() if len(input) < 2 { t.Fatal("truncated token field length") } length := int(binary.LittleEndian.Uint16(input[:2])) if len(input) < 2+length { t.Fatal("truncated token field") } return input[2 : 2+length], input[2+length:] }