Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support hello auth version "2.0" with JWT #251

Merged
merged 5 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const (
HeaderBackendSignalingRandom = "Spreed-Signaling-Random"
HeaderBackendSignalingChecksum = "Spreed-Signaling-Checksum"
HeaderBackendServer = "Spreed-Signaling-Backend"

ConfigGroupSignaling = "signaling"

ConfigKeyHelloV2TokenKey = "hello-v2-token-key"
ConfigKeySessionPingLimit = "session-ping-limit"
)

func newRandomString(length int) string {
Expand Down
2 changes: 1 addition & 1 deletion api_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ type HelloProxyClientMessage struct {
}

func (m *HelloProxyClientMessage) CheckValid() error {
if m.Version != HelloVersion {
if m.Version != HelloVersionV1 {
return fmt.Errorf("unsupported hello version: %s", m.Version)
}
if m.ResumeId == "" {
Expand Down
46 changes: 42 additions & 4 deletions api_signaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ import (
"net/url"
"sort"
"strings"

"github.com/golang-jwt/jwt/v4"
)

const (
// Version that must be sent in a "hello" message.
HelloVersion = "1.0"
// Version 1.0 validates auth params against the Nextcloud instance.
HelloVersionV1 = "1.0"

// Version 2.0 validates auth params encoded as JWT.
HelloVersionV2 = "2.0"
)

// ClientMessage is a message that is sent from a client to the server.
Expand Down Expand Up @@ -325,6 +330,23 @@ func (p *ClientTypeInternalAuthParams) CheckValid() error {
return nil
}

type HelloV2AuthParams struct {
Token string `json:"token"`
}

func (p *HelloV2AuthParams) CheckValid() error {
if p.Token == "" {
return fmt.Errorf("token missing")
}
return nil
}

type HelloV2TokenClaims struct {
jwt.RegisteredClaims

UserData *json.RawMessage `json:"userdata,omitempty"`
}

type HelloClientMessageAuth struct {
// The client type that is connecting. Leave empty to use the default
// "HelloClientTypeClient"
Expand All @@ -336,6 +358,7 @@ type HelloClientMessageAuth struct {
parsedUrl *url.URL

internalParams ClientTypeInternalAuthParams
helloV2Params HelloV2AuthParams
}

// Type "hello"
Expand All @@ -352,8 +375,8 @@ type HelloClientMessage struct {
}

func (m *HelloClientMessage) CheckValid() error {
if m.Version != HelloVersion {
return fmt.Errorf("unsupported hello version: %s", m.Version)
if m.Version != HelloVersionV1 && m.Version != HelloVersionV2 {
return InvalidHelloVersion
}
if m.ResumeId == "" {
if m.Auth.Params == nil || len(*m.Auth.Params) == 0 {
Expand All @@ -375,6 +398,17 @@ func (m *HelloClientMessage) CheckValid() error {

m.Auth.parsedUrl = u
}

switch m.Version {
case HelloVersionV1:
// No additional validation necessary.
case HelloVersionV2:
if err := json.Unmarshal(*m.Auth.Params, &m.Auth.helloV2Params); err != nil {
return err
} else if err := m.Auth.helloV2Params.CheckValid(); err != nil {
return err
}
}
case HelloClientTypeInternal:
if err := json.Unmarshal(*m.Auth.Params, &m.Auth.internalParams); err != nil {
return err
Expand All @@ -397,6 +431,7 @@ const (
ServerFeatureTransientData = "transient-data"
ServerFeatureInCallAll = "incall-all"
ServerFeatureWelcome = "welcome"
ServerFeatureHelloV2 = "hello-v2"

// Features for internal clients only.
ServerFeatureInternalVirtualSessions = "virtual-sessions"
Expand All @@ -408,19 +443,22 @@ var (
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
ServerFeatureHelloV2,
}
DefaultFeaturesInternal = []string{
ServerFeatureInternalVirtualSessions,
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
ServerFeatureHelloV2,
}
DefaultWelcomeFeatures = []string{
ServerFeatureAudioVideoPermissions,
ServerFeatureInternalVirtualSessions,
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
ServerFeatureHelloV2,
}
)

Expand Down
79 changes: 68 additions & 11 deletions api_signaling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,78 +90,135 @@ func TestClientMessage(t *testing.T) {

func TestHelloClientMessage(t *testing.T) {
internalAuthParams := []byte("{\"backend\":\"https://domain.invalid\"}")
tokenAuthParams := []byte("{\"token\":\"invalid-token\"}")
valid_messages := []testCheckValid{
// Hello version 1
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'},
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Type: "client",
Params: &json.RawMessage{'{', '}'},
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Type: "internal",
Params: (*json.RawMessage)(&internalAuthParams),
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
ResumeId: "the-resume-id",
},
// Hello version 2
&HelloClientMessage{
Version: HelloVersionV2,
Auth: HelloClientMessageAuth{
Params: (*json.RawMessage)(&tokenAuthParams),
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: HelloClientMessageAuth{
Type: "client",
Params: (*json.RawMessage)(&tokenAuthParams),
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
ResumeId: "the-resume-id",
},
}
invalid_messages := []testCheckValid{
// Hello version 1
&HelloClientMessage{},
&HelloClientMessage{Version: "0.0"},
&HelloClientMessage{Version: HelloVersion},
&HelloClientMessage{Version: HelloVersionV1},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'},
Type: "invalid-type",
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'},
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'},
Url: "invalid-url",
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Type: "internal",
Params: &json.RawMessage{'{', '}'},
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
Auth: HelloClientMessageAuth{
Type: "internal",
Params: &json.RawMessage{'x', 'y', 'z'}, // Invalid JSON.
},
},
// Hello version 2
&HelloClientMessage{
Version: HelloVersionV2,
Auth: HelloClientMessageAuth{
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: HelloClientMessageAuth{
Params: (*json.RawMessage)(&tokenAuthParams),
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: HelloClientMessageAuth{
Params: (*json.RawMessage)(&tokenAuthParams),
Url: "invalid-url",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: HelloClientMessageAuth{
Params: (*json.RawMessage)(&internalAuthParams),
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'x', 'y', 'z'}, // Invalid JSON.
Url: "https://domain.invalid",
},
},
}

testMessages(t, "hello", valid_messages, invalid_messages)
Expand Down
Loading