Skip to content

Commit 51b2dbd

Browse files
author
Jannis Pohlmann
committed
connections: Add user information to connections based on authToken
This is done via a new UserFromAuthToken function that's passed to the connection as part of its (also new) connection config. When the auth token is received from the GQL_CONNECTION_INIT message, this hook is called and the returned user (can be anything) is stored in the connection. It can later be retrieved from the connection via the User() method.
1 parent 246b6af commit 51b2dbd

File tree

1 file changed

+55
-14
lines changed

1 file changed

+55
-14
lines changed

connections.go

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package graphqlws
33
import (
44
"encoding/json"
55
"errors"
6+
"fmt"
67
"time"
78

89
"github.com/google/uuid"
@@ -30,6 +31,12 @@ const (
3031
writeTimeout = 10 * time.Second
3132
)
3233

34+
// InitMessagePayload defines the parameters of a connection
35+
// init message.
36+
type InitMessagePayload struct {
37+
AuthToken string `json:"authToken"`
38+
}
39+
3340
// StartMessagePayload defines the parameters of an operation that
3441
// a client requests to be started.
3542
type StartMessagePayload struct {
@@ -59,6 +66,10 @@ func (msg OperationMessage) String() string {
5966
return "<invalid>"
6067
}
6168

69+
// UserFromAuthTokenFunc is a function that resolves an auth token
70+
// into a user (or returns an error if that isn't possible).
71+
type UserFromAuthTokenFunc func(token string) (interface{}, error)
72+
6273
// ConnectionEventHandlers define the event handlers for a connection.
6374
// Event handlers allow other system components to react to events such
6475
// as the connection closing or an operation being started or stopped.
@@ -81,12 +92,22 @@ type ConnectionEventHandlers struct {
8192
StopOperation func(Connection, string)
8293
}
8394

95+
// ConnectionConfig defines the configuration parameters of a
96+
// GraphQL WebSocket connection.
97+
type ConnectionConfig struct {
98+
UserFromAuthToken UserFromAuthTokenFunc
99+
EventHandlers ConnectionEventHandlers
100+
}
101+
84102
// Connection is an interface to represent GraphQL WebSocket connections.
85103
// Each connection is associated with an ID that is unique to the server.
86104
type Connection interface {
87105
// ID returns the unique ID of the connection.
88106
ID() string
89107

108+
// User returns the user associated with the connection (or nil).
109+
User() interface{}
110+
90111
// SendData sends results of executing an operation (typically a
91112
// subscription) to the client.
92113
SendData(string, *DataMessagePayload)
@@ -100,11 +121,12 @@ type Connection interface {
100121
*/
101122

102123
type connection struct {
103-
id string
104-
ws *websocket.Conn
105-
eventHandlers *ConnectionEventHandlers
106-
logger *log.Entry
107-
outgoing chan OperationMessage
124+
id string
125+
ws *websocket.Conn
126+
config ConnectionConfig
127+
logger *log.Entry
128+
outgoing chan OperationMessage
129+
user interface{}
108130
}
109131

110132
func operationMessageForType(messageType string) OperationMessage {
@@ -116,11 +138,11 @@ func operationMessageForType(messageType string) OperationMessage {
116138
// NewConnection establishes a GraphQL WebSocket connection. It implements
117139
// the GraphQL WebSocket protocol by managing its internal state and handling
118140
// the client-server communication.
119-
func NewConnection(ws *websocket.Conn, eventHandlers *ConnectionEventHandlers) Connection {
141+
func NewConnection(ws *websocket.Conn, config ConnectionConfig) Connection {
120142
conn := new(connection)
121143
conn.id = uuid.New().String()
122144
conn.ws = ws
123-
conn.eventHandlers = eventHandlers
145+
conn.config = config
124146
conn.logger = NewLogger("connection/" + conn.id)
125147

126148
conn.outgoing = make(chan OperationMessage)
@@ -137,6 +159,10 @@ func (conn *connection) ID() string {
137159
return conn.id
138160
}
139161

162+
func (conn *connection) User() interface{} {
163+
return conn.user
164+
}
165+
140166
func (conn *connection) SendData(opID string, data *DataMessagePayload) {
141167
msg := operationMessageForType(gqlData)
142168
msg.ID = opID
@@ -162,8 +188,8 @@ func (conn *connection) close() {
162188
close(conn.outgoing)
163189

164190
// Notify event handlers
165-
if conn.eventHandlers != nil {
166-
conn.eventHandlers.Close(conn)
191+
if conn.config.EventHandlers.Close != nil {
192+
conn.config.EventHandlers.Close(conn)
167193
}
168194

169195
conn.logger.Info("Closed connection")
@@ -238,16 +264,31 @@ func (conn *connection) readLoop() {
238264

239265
// When the GraphQL WS connection is initiated, send an ACK back
240266
case gqlConnectionInit:
241-
conn.outgoing <- operationMessageForType(gqlConnectionAck)
267+
data := InitMessagePayload{}
268+
if err := json.Unmarshal(rawPayload, &data); err != nil {
269+
conn.SendError(errors.New("Invalid GQL_CONNECTION_INIT payload"))
270+
} else {
271+
if conn.config.UserFromAuthToken != nil {
272+
user, err := conn.config.UserFromAuthToken(data.AuthToken)
273+
if err != nil {
274+
conn.SendError(fmt.Errorf("Failed to authenticate user: %v", err))
275+
} else {
276+
conn.user = user
277+
conn.outgoing <- operationMessageForType(gqlConnectionAck)
278+
}
279+
} else {
280+
conn.outgoing <- operationMessageForType(gqlConnectionAck)
281+
}
282+
}
242283

243284
// Let event handlers deal with starting operations
244285
case gqlStart:
245-
if conn.eventHandlers != nil {
286+
if conn.config.EventHandlers.StartOperation != nil {
246287
data := StartMessagePayload{}
247288
if err := json.Unmarshal(rawPayload, &data); err != nil {
248289
conn.SendError(errors.New("Invalid GQL_START payload"))
249290
} else {
250-
errs := conn.eventHandlers.StartOperation(conn, msg.ID, &data)
291+
errs := conn.config.EventHandlers.StartOperation(conn, msg.ID, &data)
251292
if errs != nil {
252293
conn.sendOperationErrors(msg.ID, errs)
253294
}
@@ -256,8 +297,8 @@ func (conn *connection) readLoop() {
256297

257298
// Let event handlers deal with stopping operations
258299
case gqlStop:
259-
if conn.eventHandlers != nil {
260-
conn.eventHandlers.StopOperation(conn, msg.ID)
300+
if conn.config.EventHandlers.StopOperation != nil {
301+
conn.config.EventHandlers.StopOperation(conn, msg.ID)
261302
}
262303

263304
// When the GraphQL WS connection is terminated by the client,

0 commit comments

Comments
 (0)