Skip to content

Commit

Permalink
Add hello version "2.0" that authenticates connections using a JWT.
Browse files Browse the repository at this point in the history
  • Loading branch information
fancycode committed Jul 7, 2022
1 parent 042d447 commit d11c0fa
Show file tree
Hide file tree
Showing 11 changed files with 695 additions and 64 deletions.
5 changes: 5 additions & 0 deletions api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,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
42 changes: 38 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"
)

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.StandardClaims

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 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
2 changes: 1 addition & 1 deletion client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ func main() {
request := &signaling.ClientMessage{
Type: "hello",
Hello: &signaling.HelloClientMessage{
Version: signaling.HelloVersion,
Version: signaling.HelloVersionV1,
Auth: signaling.HelloClientMessageAuth{
Url: backendUrl + "/auth",
Params: &json.RawMessage{'{', '}'},
Expand Down
2 changes: 1 addition & 1 deletion clientsession_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func TestBandwidth_Backend(t *testing.T) {
params := TestBackendClientAuthParams{
UserId: testDefaultUserId,
}
if err := client.SendHelloParams(server.URL+"/one", "client", params); err != nil {
if err := client.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", params); err != nil {
t.Fatal(err)
}

Expand Down
Loading

0 comments on commit d11c0fa

Please sign in to comment.