From 32708e9098e9c32c69c484d8d77afb435880862e Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 8 Jun 2017 15:08:18 -0600 Subject: [PATCH 1/2] submscription fixes --- client/coral-framework/actions/auth.js | 11 ++++++ client/coral-framework/services/client.js | 42 +++++++++++++++++++++-- graph/subscriptions.js | 28 ++++++++++++--- services/subscriptions.js | 32 ++++++----------- 4 files changed, 84 insertions(+), 29 deletions(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 91f709b35..fe6546296 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -4,6 +4,7 @@ import * as actions from '../constants/auth'; import * as Storage from '../helpers/storage'; import coralApi, {base} from '../helpers/request'; +import {resetWebsocket} from 'coral-framework/services/client'; import t from 'coral-framework/services/i18n'; export const showSignInDialog = () => (dispatch, getState) => { @@ -117,6 +118,12 @@ const signInFailure = (error) => ({ export const handleAuthToken = (token) => (dispatch) => { Storage.setItem('exp', jwtDecode(token).exp); Storage.setItem('token', token); + + alert('handled the auth token!'); + + // Reset the websocket. + resetWebsocket(); + dispatch({type: 'HANDLE_AUTH_TOKEN'}); }; @@ -277,6 +284,10 @@ export const logout = () => (dispatch) => { if (!bowser.safari && !bowser.ios) { Storage.removeItem('token'); } + + // Reset the websocket. + resetWebsocket(); + dispatch({type: actions.LOGOUT}); }); }; diff --git a/client/coral-framework/services/client.js b/client/coral-framework/services/client.js index 6095e17ab..70408248c 100644 --- a/client/coral-framework/services/client.js +++ b/client/coral-framework/services/client.js @@ -1,8 +1,31 @@ import ApolloClient, {addTypename} from 'apollo-client'; import {networkInterface} from './transport'; import {SubscriptionClient, addGraphQLSubscriptions} from 'subscriptions-transport-ws'; +import {SUBSCRIPTION_END} from 'subscriptions-transport-ws/dist/messageTypes'; +import {getAuthToken} from '../helpers/request'; -let client; +let client, wsClient = null, wsClientToken = null; + +export function resetWebsocket() { + if (wsClient === null) { + + // Nothing to reset! + return; + } + + // Unsubscribe from all the active subscriptions. + Object.keys(wsClient.subscriptions).forEach((id) => { + + // Create the message. + let message = {id: parseInt(id), type: SUBSCRIPTION_END}; + + // Send the unsubscribe message. + wsClient.client.send(JSON.stringify(message)); + }); + + // Close the client, this will trigger a reconnect. + wsClient.close(); +} export function getClient() { if (client) { @@ -10,8 +33,21 @@ export function getClient() { } const protocol = location.protocol === 'https:' ? 'wss' : 'ws'; - const wsClient = new SubscriptionClient(`${protocol}://${location.host}/api/v1/live`, { - reconnect: true + wsClient = new SubscriptionClient(`${protocol}://${location.host}/api/v1/live`, { + reconnect: true, + connectionParams: { + get token() { + + wsClientToken = getAuthToken(); + + // Try to get the token from localStorage. If it isn't here, it may + // be passed as a cookie. + + // NOTE: THIS IS ONLY EVER EVALUATED ONCE, IN ORDER TO SEND A DIFFERNT + // TOKEN YOU MUST DISCONNECT AND RECONNECT THE WEBSOCKET CLIENT. + return wsClientToken; + } + } }); const networkInterfaceWithSubscriptions = addGraphQLSubscriptions( diff --git a/graph/subscriptions.js b/graph/subscriptions.js index 0442c237d..0cc37e841 100644 --- a/graph/subscriptions.js +++ b/graph/subscriptions.js @@ -41,16 +41,34 @@ const createSubscriptionManager = (server) => new SubscriptionServer({ pubsub, setupFunctions, }), + onConnect: ({token}, connection) => { + + // Attach the token from the connection options if it was provided. + if (token) { + + // Attach it to the upgrade request. + connection.upgradeReq.headers['authorization'] = `Bearer ${token}`; + } + }, onSubscribe: (parsedMessage, baseParams, connection) => { + // Cache the upgrade request. + let upgradeReq = connection.upgradeReq; + // Attach the context per request. - baseParams.context = () => deserializeUser(connection.upgradeReq) - .then((req) => new Context(req, pubsub)) - .catch((err) => { - console.error(err); + baseParams.context = async () => { + let req; + + try { + req = await deserializeUser(upgradeReq); + } catch (e) { + console.error(e); return new Context({}, pubsub); - }); + } + + return new Context(req, pubsub); + }; return baseParams; } diff --git a/services/subscriptions.js b/services/subscriptions.js index c4cd2378a..d6a5b430c 100644 --- a/services/subscriptions.js +++ b/services/subscriptions.js @@ -1,4 +1,4 @@ -const passport = require('./passport'); +const cookieParser = require('cookie-parser')(); const authentication = require('../middleware/authentication'); // Session data does not automatically attach to websocket req objects. @@ -7,32 +7,22 @@ const authentication = require('../middleware/authentication'); const deserializeUser = (req) => { return new Promise((resolve, reject) => { - // This uses the authentication connect middleware to establish the session - // user details from the headers. - authentication(req, null, (err) => { + // Parse the cookies (if they exist?). + cookieParser(req, null, (err) => { if (err) { return reject(err); } - if ('session' in req && 'passport' in req.session && 'user' in req.session.passport) { - passport.deserializeUser(req.session.passport.user, (err, user) => { - if (err) { - return reject(err); - } - - req.user = user; - - return resolve(req); - }); - } - - // Remove the user from the request (if there was one) - if (req.user) { - delete req.user; - } + // This uses the authentication connect middleware to establish the + // current user. + authentication(req, null, (err) => { + if (err) { + return reject(err); + } // Resolve with the request (user removed possibly). - return resolve(req); + return resolve(req); + }); }); }); }; From c7ca3c5e4a33100d87fd0e6f24bd72b8337aefb4 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 9 Jun 2017 17:26:42 +0700 Subject: [PATCH 2/2] Reset websocket after successful login --- client/coral-framework/actions/auth.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index fe6546296..e212af99b 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -119,11 +119,6 @@ export const handleAuthToken = (token) => (dispatch) => { Storage.setItem('exp', jwtDecode(token).exp); Storage.setItem('token', token); - alert('handled the auth token!'); - - // Reset the websocket. - resetWebsocket(); - dispatch({type: 'HANDLE_AUTH_TOKEN'}); }; @@ -318,6 +313,9 @@ export const checkLogin = () => (dispatch) => { dispatch(checkLoginSuccess(result.user)); + // Reset the websocket. + resetWebsocket(); + // Display create username dialog if necessary. if (result.user.canEditName && result.user.status !== 'BANNED') { dispatch(showCreateUsernameDialog());