From bd21719ea50dddcd8da60602cd6023a5f5e899d7 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 24 May 2017 17:20:23 -0300 Subject: [PATCH 01/18] Handling non safari browsers --- client/coral-admin/src/actions/auth.js | 42 +++++++++++++++------- client/coral-framework/actions/auth.js | 24 ++++++++----- client/coral-framework/helpers/response.js | 27 ++++++++------ package.json | 1 + yarn.lock | 4 +++ 5 files changed, 67 insertions(+), 31 deletions(-) diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 29971e294..00ab46910 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,28 +1,44 @@ +import browser from 'detect-browser'; import * as actions from '../constants/auth'; -import * as Storage from 'coral-framework/helpers/storage'; import coralApi from 'coral-framework/helpers/response'; +import * as Storage from 'coral-framework/helpers/storage'; import {handleAuthToken} from 'coral-framework/actions/auth'; //============================================================================== // SIGN IN //============================================================================== -export const handleLogin = (email, password, recaptchaResponse) => (dispatch) => { +export const handleLogin = (email, password, recaptchaResponse) => dispatch => { dispatch({type: actions.LOGIN_REQUEST}); - const params = {method: 'POST', body: {email, password}}; + + const params = { + method: 'POST', + body: { + email, + password + } + }; + if (recaptchaResponse) { - params.headers = {'X-Recaptcha-Response': recaptchaResponse}; + params.headers = { + 'X-Recaptcha-Response': recaptchaResponse + }; } + return coralApi('/auth/local', params) .then(({user, token}) => { + if (!user) { - Storage.removeItem('token'); + if (!browser || browser.name !== 'safari') { + Storage.removeItem('token'); + } return dispatch(checkLoginFailure('not logged in')); } + dispatch(handleAuthToken(token)); dispatch(checkLoginSuccess(user)); }) - .catch((error) => { + .catch(error => { if (error.translation_key === 'LOGIN_MAXIMUM_EXCEEDED') { dispatch({ type: actions.LOGIN_MAXIMUM_EXCEEDED, @@ -50,11 +66,11 @@ const forgotPassowordFailure = () => ({ type: actions.FETCH_FORGOT_PASSWORD_FAILURE }); -export const requestPasswordReset = (email) => (dispatch) => { +export const requestPasswordReset = email => dispatch => { dispatch(forgotPassowordRequest(email)); return coralApi('/account/password/reset', {method: 'POST', body: {email}}) .then(() => dispatch(forgotPassowordSuccess())) - .catch((error) => dispatch(forgotPassowordFailure(error))); + .catch(error => dispatch(forgotPassowordFailure(error))); }; //============================================================================== @@ -71,23 +87,25 @@ const checkLoginSuccess = (user, isAdmin) => ({ isAdmin }); -const checkLoginFailure = (error) => ({ +const checkLoginFailure = error => ({ type: actions.CHECK_LOGIN_FAILURE, error }); -export const checkLogin = () => (dispatch) => { +export const checkLogin = () => dispatch => { dispatch(checkLoginRequest()); return coralApi('/auth') .then(({user}) => { if (!user) { - Storage.removeItem('token'); + if (!browser || browser.name !== 'safari') { + Storage.removeItem('token'); + } return dispatch(checkLoginFailure('not logged in')); } dispatch(checkLoginSuccess(user)); }) - .catch((error) => { + .catch(error => { console.error(error); dispatch(checkLoginFailure(`${error.translation_key}`)); }); diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 6b71fa9dd..071235ce3 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -1,14 +1,14 @@ -import {pym} from 'coral-framework'; -import * as Storage from '../helpers/storage'; -import * as actions from '../constants/auth'; -import coralApi, {base} from '../helpers/response'; import jwtDecode from 'jwt-decode'; +import {pym} from 'coral-framework'; +import browser from 'detect-browser'; +import * as actions from '../constants/auth'; +import * as Storage from '../helpers/storage'; +import coralApi, {base} from '../helpers/response'; const lang = new I18n(translations); import translations from './../translations'; import I18n from '../../coral-framework/modules/i18n/i18n'; -// Dialog Actions export const showSignInDialog = () => (dispatch) => { const signInPopUp = window.open( '/embed/stream/login', @@ -112,8 +112,10 @@ const signInFailure = (error) => ({ //============================================================================== export const handleAuthToken = (token) => (dispatch) => { - Storage.setItem('exp', jwtDecode(token).exp); - Storage.setItem('token', token); + if (!browser || browser.name !== 'safari') { + Storage.setItem('exp', jwtDecode(token).exp); + Storage.setItem('token', token); + } dispatch({type: 'HANDLE_AUTH_TOKEN'}); }; @@ -269,7 +271,9 @@ export const fetchForgotPassword = (email) => (dispatch) => { export const logout = () => (dispatch) => { return coralApi('/auth', {method: 'DELETE'}).then(() => { - Storage.removeItem('token'); + if (!browser || browser.name !== 'safari') { + Storage.removeItem('token'); + } dispatch({type: actions.LOGOUT}); }); }; @@ -292,7 +296,9 @@ export const checkLogin = () => (dispatch) => { coralApi('/auth') .then((result) => { if (!result.user) { - Storage.removeItem('token'); + if (!browser || browser.name !== 'safari') { + Storage.removeItem('token'); + } throw new Error('Not logged in'); } diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/response.js index 794a73e30..bad2e52f7 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/response.js @@ -1,22 +1,29 @@ -import * as Storage from './storage'; +import browser from 'detect-browser'; const buildOptions = (inputOptions = {}) => { const defaultOptions = { method: 'GET', headers: { Accept: 'application/json', - Authorization: `Bearer ${Storage.getItem('token')}`, 'Content-Type': 'application/json' }, credentials: 'same-origin' }; - let options = Object.assign({}, defaultOptions, inputOptions); - options.headers = Object.assign( - {}, - defaultOptions.headers, - inputOptions.headers - ); + let options = { + defaultOptions, + ...inputOptions + }; + + if (!browser || browser.name !== 'safari') { + let authorization = localStorage.getItem('token'); + + if (authorization) { + options.headers = { + Authorization: `Bearer ${authorization}` + }; + } + } if (options.method.toLowerCase() !== 'get') { options.body = JSON.stringify(options.body); @@ -25,9 +32,9 @@ const buildOptions = (inputOptions = {}) => { return options; }; -const handleResp = (res) => { +const handleResp = res => { if (res.status > 399) { - return res.json().then((err) => { + return res.json().then(err => { let message = err.message || res.status; const error = new Error(); diff --git a/package.json b/package.json index 84d0fdf52..cbf514cf5 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "csurf": "^1.9.0", "dataloader": "^1.3.0", "debug": "^2.6.3", + "detect-browser": "^1.7.0", "dotenv": "^4.0.0", "ejs": "^2.5.6", "env-rewrite": "^1.0.2", diff --git a/yarn.lock b/yarn.lock index 728687248..3fd71e907 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2390,6 +2390,10 @@ destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" +detect-browser@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-1.7.0.tgz#11758cd6aa07d76c25784036d19154ae0392c3b3" + detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" From bb6be9a7345d7f1b96a13a3e62ad4d9d351b4d18 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Thu, 25 May 2017 11:53:40 -0300 Subject: [PATCH 02/18] BE implementation --- client/coral-framework/actions/auth.js | 108 +++++++++++---------- client/coral-framework/helpers/response.js | 4 +- routes/api/auth/index.js | 29 +++++- services/passport.js | 7 ++ 4 files changed, 90 insertions(+), 58 deletions(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 071235ce3..7798e165f 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -9,7 +9,7 @@ const lang = new I18n(translations); import translations from './../translations'; import I18n from '../../coral-framework/modules/i18n/i18n'; -export const showSignInDialog = () => (dispatch) => { +export const showSignInDialog = () => dispatch => { const signInPopUp = window.open( '/embed/stream/login', 'Login', @@ -32,7 +32,7 @@ export const showSignInDialog = () => (dispatch) => { dispatch({type: actions.SHOW_SIGNIN_DIALOG}); }; -export const hideSignInDialog = () => (dispatch) => { +export const hideSignInDialog = () => dispatch => { dispatch({type: actions.HIDE_SIGNIN_DIALOG}); window.close(); }; @@ -51,7 +51,7 @@ const createUsernameSuccess = () => ({ type: actions.CREATE_USERNAME_SUCCESS }); -const createUsernameFailure = (error) => ({ +const createUsernameFailure = error => ({ type: actions.CREATE_USERNAME_FAILURE, error }); @@ -61,7 +61,7 @@ export const updateUsername = ({username}) => ({ username }); -export const createUsername = (userId, formData) => (dispatch) => { +export const createUsername = (userId, formData) => dispatch => { dispatch(createUsernameRequest()); coralApi('/account/username', {method: 'PUT', body: formData}) .then(() => { @@ -69,26 +69,26 @@ export const createUsername = (userId, formData) => (dispatch) => { dispatch(hideCreateUsernameDialog()); dispatch(updateUsername(formData)); }) - .catch((error) => { + .catch(error => { dispatch(createUsernameFailure(lang.t(`error.${error.translation_key}`))); }); }; -export const changeView = (view) => (dispatch) => { +export const changeView = view => dispatch => { dispatch({ type: actions.CHANGE_VIEW, view }); switch (view) { - case 'SIGNUP': - window.resizeTo(500, 800); - break; - case 'FORGOT': - window.resizeTo(500, 400); - break; - default: - window.resizeTo(500, 550); + case 'SIGNUP': + window.resizeTo(500, 800); + break; + case 'FORGOT': + window.resizeTo(500, 400); + break; + default: + window.resizeTo(500, 550); } }; @@ -102,7 +102,7 @@ const signInRequest = () => ({ type: actions.FETCH_SIGNIN_REQUEST }); -const signInFailure = (error) => ({ +const signInFailure = error => ({ type: actions.FETCH_SIGNIN_FAILURE, error }); @@ -111,7 +111,7 @@ const signInFailure = (error) => ({ // AUTH TOKEN //============================================================================== -export const handleAuthToken = (token) => (dispatch) => { +export const handleAuthToken = token => dispatch => { if (!browser || browser.name !== 'safari') { Storage.setItem('exp', jwtDecode(token).exp); Storage.setItem('token', token); @@ -123,26 +123,29 @@ export const handleAuthToken = (token) => (dispatch) => { // SIGN IN //============================================================================== -export const fetchSignIn = (formData) => (dispatch) => { - dispatch(signInRequest()); - return coralApi('/auth/local', {method: 'POST', body: formData}) - .then(({token}) => { - dispatch(handleAuthToken(token)); - dispatch(hideSignInDialog()); - }) - .catch((error) => { - if (error.metadata) { +export const fetchSignIn = formData => { + return dispatch => { + dispatch(signInRequest()); - // the user might not have a valid email. prompt the user user re-request the confirmation email - dispatch( - signInFailure(lang.t('error.emailNotVerified', error.metadata)) - ); - } else { + return coralApi('/auth/local', {method: 'POST', body: formData}) + .then(({token}) => { + dispatch(handleAuthToken(token)); + dispatch(hideSignInDialog()); + }) + .catch(error => { + if (error.metadata) { - // invalid credentials - dispatch(signInFailure(lang.t('error.emailPasswordError'))); - } - }); + // the user might not have a valid email. prompt the user user re-request the confirmation email + dispatch( + signInFailure(lang.t('error.emailNotVerified', error.metadata)) + ); + } else { + + // invalid credentials + dispatch(signInFailure(lang.t('error.emailPasswordError'))); + } + }); + }; }; //============================================================================== @@ -153,17 +156,17 @@ const signInFacebookRequest = () => ({ type: actions.FETCH_SIGNIN_FACEBOOK_REQUEST }); -const signInFacebookSuccess = (user) => ({ +const signInFacebookSuccess = user => ({ type: actions.FETCH_SIGNIN_FACEBOOK_SUCCESS, user }); -const signInFacebookFailure = (error) => ({ +const signInFacebookFailure = error => ({ type: actions.FETCH_SIGNIN_FACEBOOK_FAILURE, error }); -export const fetchSignInFacebook = () => (dispatch) => { +export const fetchSignInFacebook = () => dispatch => { dispatch(signInFacebookRequest()); window.open( `${base}/auth/facebook`, @@ -180,7 +183,7 @@ const signUpFacebookRequest = () => ({ type: actions.FETCH_SIGNUP_FACEBOOK_REQUEST }); -export const fetchSignUpFacebook = () => (dispatch) => { +export const fetchSignUpFacebook = () => dispatch => { dispatch(signUpFacebookRequest()); window.open( `${base}/auth/facebook`, @@ -213,10 +216,10 @@ export const facebookCallback = (err, data) => (dispatch, getState) => { //============================================================================== const signUpRequest = () => ({type: actions.FETCH_SIGNUP_REQUEST}); -const signUpSuccess = (user) => ({type: actions.FETCH_SIGNUP_SUCCESS, user}); -const signUpFailure = (error) => ({type: actions.FETCH_SIGNUP_FAILURE, error}); +const signUpSuccess = user => ({type: actions.FETCH_SIGNUP_SUCCESS, user}); +const signUpFailure = error => ({type: actions.FETCH_SIGNUP_FAILURE, error}); -export const fetchSignUp = (formData, redirectUri) => (dispatch) => { +export const fetchSignUp = (formData, redirectUri) => dispatch => { dispatch(signUpRequest()); coralApi('/users', { @@ -227,7 +230,7 @@ export const fetchSignUp = (formData, redirectUri) => (dispatch) => { .then(({user}) => { dispatch(signUpSuccess(user)); }) - .catch((error) => { + .catch(error => { let errorMessage = lang.t(`error.${error.message}`); // if there is no translation defined, just show the error string @@ -254,7 +257,7 @@ const forgotPasswordFailure = () => ({ type: actions.FETCH_FORGOT_PASSWORD_FAILURE }); -export const fetchForgotPassword = (email) => (dispatch) => { +export const fetchForgotPassword = email => dispatch => { dispatch(forgotPasswordRequest(email)); const redirectUri = pym.parentUrl || location.href; coralApi('/account/password/reset', { @@ -262,14 +265,14 @@ export const fetchForgotPassword = (email) => (dispatch) => { body: {email, loc: redirectUri} }) .then(() => dispatch(forgotPasswordSuccess())) - .catch((error) => dispatch(forgotPasswordFailure(error))); + .catch(error => dispatch(forgotPasswordFailure(error))); }; //============================================================================== // LOGOUT //============================================================================== -export const logout = () => (dispatch) => { +export const logout = () => dispatch => { return coralApi('/auth', {method: 'DELETE'}).then(() => { if (!browser || browser.name !== 'safari') { Storage.removeItem('token'); @@ -283,7 +286,7 @@ export const logout = () => (dispatch) => { //============================================================================== const checkLoginRequest = () => ({type: actions.CHECK_LOGIN_REQUEST}); -const checkLoginFailure = (error) => ({type: actions.CHECK_LOGIN_FAILURE, error}); +const checkLoginFailure = error => ({type: actions.CHECK_LOGIN_FAILURE, error}); const checkLoginSuccess = (user, isAdmin) => ({ type: actions.CHECK_LOGIN_SUCCESS, @@ -291,10 +294,10 @@ const checkLoginSuccess = (user, isAdmin) => ({ isAdmin }); -export const checkLogin = () => (dispatch) => { +export const checkLogin = () => dispatch => { dispatch(checkLoginRequest()); coralApi('/auth') - .then((result) => { + .then(result => { if (!result.user) { if (!browser || browser.name !== 'safari') { Storage.removeItem('token'); @@ -304,14 +307,14 @@ export const checkLogin = () => (dispatch) => { dispatch(checkLoginSuccess(result.user)); }) - .catch((error) => { + .catch(error => { console.error(error); dispatch(checkLoginFailure(`${error.translation_key}`)); }); }; export const validForm = () => ({type: actions.VALID_FORM}); -export const invalidForm = (error) => ({type: actions.INVALID_FORM, error}); +export const invalidForm = error => ({type: actions.INVALID_FORM, error}); //============================================================================== // VERIFY EMAIL @@ -329,7 +332,7 @@ const verifyEmailFailure = () => ({ type: actions.VERIFY_EMAIL_FAILURE }); -export const requestConfirmEmail = (email, redirectUri) => (dispatch) => { +export const requestConfirmEmail = (email, redirectUri) => dispatch => { dispatch(verifyEmailRequest()); return coralApi('/users/resend-verify', { method: 'POST', @@ -339,8 +342,7 @@ export const requestConfirmEmail = (email, redirectUri) => (dispatch) => { .then(() => { dispatch(verifyEmailSuccess()); }) - .catch((err) => { - + .catch(err => { // email might have already been verifyed dispatch(verifyEmailFailure(err)); }); diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/response.js index bad2e52f7..b6e48abb3 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/response.js @@ -5,13 +5,13 @@ const buildOptions = (inputOptions = {}) => { method: 'GET', headers: { Accept: 'application/json', - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, credentials: 'same-origin' }; let options = { - defaultOptions, + ...defaultOptions, ...inputOptions }; diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js index 6f610e2b0..90fe31b1e 100644 --- a/routes/api/auth/index.js +++ b/routes/api/auth/index.js @@ -3,6 +3,14 @@ const {passport, HandleGenerateCredentials, HandleLogout} = require('../../../se const router = express.Router(); +const lookup = (i) => { + switch (i) { + case 0: return 'header'; + case 1: return 'cookie'; + case 2: return 'query'; + } +}; + /** * This returns the user if they are logged in. */ @@ -15,16 +23,31 @@ router.get('/', (req, res, next) => { res.status(204).end(); }, (req, res) => { - // Send back the user object. - res.json({user: req.user}); + const authorizations = [ + req.headers.authorization, + req.cookies ? req.cookies.authorization : [], + req.query.authorization + ]; + + res.set('Cache-Control', 'no-cache, no-store, must-revalidate'); + + let i = authorizations.findIndex((source) => source !== null && typeof source != 'undefined' && source.length > 0); + if (i >= 0) { + let authorization = authorizations[i]; + let source = lookup(i); + + // Send back the user object. + res.json({authorization, source, user: req.user}); + } }); /** * This blacklists the token used to authenticate. */ + router.delete('/', HandleLogout); -//============================================================================== +// ============================================================================= // PASSPORT ROUTES //============================================================================== diff --git a/services/passport.js b/services/passport.js index c64bb2334..5e993be51 100644 --- a/services/passport.js +++ b/services/passport.js @@ -47,6 +47,12 @@ const HandleGenerateCredentials = (req, res, next) => (err, user) => { // Generate the token to re-issue to the frontend. const token = GenerateToken(user); + // handling cookie + res.cookie('authorization', token, { + httpOnly: true, + expires: new Date(Date.now() + 900000) + }); + // Send back the details! res.json({user, token}); }; @@ -134,6 +140,7 @@ const HandleLogout = (req, res, next) => { return next(err); } + res.clearCookie('authorization'); res.status(204).end(); }); }; From e510553d9a7fb9accafb92f6c8884878bc2ccb9f Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Thu, 25 May 2017 13:55:03 -0300 Subject: [PATCH 03/18] Sending cookies if safari --- client/coral-admin/src/actions/auth.js | 20 +++--- client/coral-framework/actions/auth.js | 83 +++++++++++----------- client/coral-framework/helpers/response.js | 4 +- routes/api/auth/index.js | 50 +++++++------ services/passport.js | 15 ++-- 5 files changed, 94 insertions(+), 78 deletions(-) diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 00ab46910..16b6c774a 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,4 +1,4 @@ -import browser from 'detect-browser'; +import browser from 'bowser'; import * as actions from '../constants/auth'; import coralApi from 'coral-framework/helpers/response'; import * as Storage from 'coral-framework/helpers/storage'; @@ -8,7 +8,7 @@ import {handleAuthToken} from 'coral-framework/actions/auth'; // SIGN IN //============================================================================== -export const handleLogin = (email, password, recaptchaResponse) => dispatch => { +export const handleLogin = (email, password, recaptchaResponse) => (dispatch) => { dispatch({type: actions.LOGIN_REQUEST}); const params = { @@ -29,7 +29,7 @@ export const handleLogin = (email, password, recaptchaResponse) => dispatch => { .then(({user, token}) => { if (!user) { - if (!browser || browser.name !== 'safari') { + if (!browser || browser.name !== 'Safari') { Storage.removeItem('token'); } return dispatch(checkLoginFailure('not logged in')); @@ -38,7 +38,7 @@ export const handleLogin = (email, password, recaptchaResponse) => dispatch => { dispatch(handleAuthToken(token)); dispatch(checkLoginSuccess(user)); }) - .catch(error => { + .catch((error) => { if (error.translation_key === 'LOGIN_MAXIMUM_EXCEEDED') { dispatch({ type: actions.LOGIN_MAXIMUM_EXCEEDED, @@ -66,11 +66,11 @@ const forgotPassowordFailure = () => ({ type: actions.FETCH_FORGOT_PASSWORD_FAILURE }); -export const requestPasswordReset = email => dispatch => { +export const requestPasswordReset = (email) => (dispatch) => { dispatch(forgotPassowordRequest(email)); return coralApi('/account/password/reset', {method: 'POST', body: {email}}) .then(() => dispatch(forgotPassowordSuccess())) - .catch(error => dispatch(forgotPassowordFailure(error))); + .catch((error) => dispatch(forgotPassowordFailure(error))); }; //============================================================================== @@ -87,17 +87,17 @@ const checkLoginSuccess = (user, isAdmin) => ({ isAdmin }); -const checkLoginFailure = error => ({ +const checkLoginFailure = (error) => ({ type: actions.CHECK_LOGIN_FAILURE, error }); -export const checkLogin = () => dispatch => { +export const checkLogin = () => (dispatch) => { dispatch(checkLoginRequest()); return coralApi('/auth') .then(({user}) => { if (!user) { - if (!browser || browser.name !== 'safari') { + if (!browser || browser.name !== 'Safari') { Storage.removeItem('token'); } return dispatch(checkLoginFailure('not logged in')); @@ -105,7 +105,7 @@ export const checkLogin = () => dispatch => { dispatch(checkLoginSuccess(user)); }) - .catch(error => { + .catch((error) => { console.error(error); dispatch(checkLoginFailure(`${error.translation_key}`)); }); diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 7798e165f..2f0a2dc0f 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -1,6 +1,6 @@ import jwtDecode from 'jwt-decode'; import {pym} from 'coral-framework'; -import browser from 'detect-browser'; +import browser from 'bowser'; import * as actions from '../constants/auth'; import * as Storage from '../helpers/storage'; import coralApi, {base} from '../helpers/response'; @@ -9,7 +9,7 @@ const lang = new I18n(translations); import translations from './../translations'; import I18n from '../../coral-framework/modules/i18n/i18n'; -export const showSignInDialog = () => dispatch => { +export const showSignInDialog = () => (dispatch) => { const signInPopUp = window.open( '/embed/stream/login', 'Login', @@ -32,7 +32,7 @@ export const showSignInDialog = () => dispatch => { dispatch({type: actions.SHOW_SIGNIN_DIALOG}); }; -export const hideSignInDialog = () => dispatch => { +export const hideSignInDialog = () => (dispatch) => { dispatch({type: actions.HIDE_SIGNIN_DIALOG}); window.close(); }; @@ -51,7 +51,7 @@ const createUsernameSuccess = () => ({ type: actions.CREATE_USERNAME_SUCCESS }); -const createUsernameFailure = error => ({ +const createUsernameFailure = (error) => ({ type: actions.CREATE_USERNAME_FAILURE, error }); @@ -61,7 +61,7 @@ export const updateUsername = ({username}) => ({ username }); -export const createUsername = (userId, formData) => dispatch => { +export const createUsername = (userId, formData) => (dispatch) => { dispatch(createUsernameRequest()); coralApi('/account/username', {method: 'PUT', body: formData}) .then(() => { @@ -69,26 +69,26 @@ export const createUsername = (userId, formData) => dispatch => { dispatch(hideCreateUsernameDialog()); dispatch(updateUsername(formData)); }) - .catch(error => { + .catch((error) => { dispatch(createUsernameFailure(lang.t(`error.${error.translation_key}`))); }); }; -export const changeView = view => dispatch => { +export const changeView = (view) => (dispatch) => { dispatch({ type: actions.CHANGE_VIEW, view }); switch (view) { - case 'SIGNUP': - window.resizeTo(500, 800); - break; - case 'FORGOT': - window.resizeTo(500, 400); - break; - default: - window.resizeTo(500, 550); + case 'SIGNUP': + window.resizeTo(500, 800); + break; + case 'FORGOT': + window.resizeTo(500, 400); + break; + default: + window.resizeTo(500, 550); } }; @@ -102,7 +102,7 @@ const signInRequest = () => ({ type: actions.FETCH_SIGNIN_REQUEST }); -const signInFailure = error => ({ +const signInFailure = (error) => ({ type: actions.FETCH_SIGNIN_FAILURE, error }); @@ -111,8 +111,8 @@ const signInFailure = error => ({ // AUTH TOKEN //============================================================================== -export const handleAuthToken = token => dispatch => { - if (!browser || browser.name !== 'safari') { +export const handleAuthToken = (token) => (dispatch) => { + if (!browser || browser.name !== 'Safari') { Storage.setItem('exp', jwtDecode(token).exp); Storage.setItem('token', token); } @@ -123,8 +123,8 @@ export const handleAuthToken = token => dispatch => { // SIGN IN //============================================================================== -export const fetchSignIn = formData => { - return dispatch => { +export const fetchSignIn = (formData) => { + return (dispatch) => { dispatch(signInRequest()); return coralApi('/auth/local', {method: 'POST', body: formData}) @@ -132,7 +132,7 @@ export const fetchSignIn = formData => { dispatch(handleAuthToken(token)); dispatch(hideSignInDialog()); }) - .catch(error => { + .catch((error) => { if (error.metadata) { // the user might not have a valid email. prompt the user user re-request the confirmation email @@ -156,17 +156,17 @@ const signInFacebookRequest = () => ({ type: actions.FETCH_SIGNIN_FACEBOOK_REQUEST }); -const signInFacebookSuccess = user => ({ +const signInFacebookSuccess = (user) => ({ type: actions.FETCH_SIGNIN_FACEBOOK_SUCCESS, user }); -const signInFacebookFailure = error => ({ +const signInFacebookFailure = (error) => ({ type: actions.FETCH_SIGNIN_FACEBOOK_FAILURE, error }); -export const fetchSignInFacebook = () => dispatch => { +export const fetchSignInFacebook = () => (dispatch) => { dispatch(signInFacebookRequest()); window.open( `${base}/auth/facebook`, @@ -183,7 +183,7 @@ const signUpFacebookRequest = () => ({ type: actions.FETCH_SIGNUP_FACEBOOK_REQUEST }); -export const fetchSignUpFacebook = () => dispatch => { +export const fetchSignUpFacebook = () => (dispatch) => { dispatch(signUpFacebookRequest()); window.open( `${base}/auth/facebook`, @@ -216,10 +216,10 @@ export const facebookCallback = (err, data) => (dispatch, getState) => { //============================================================================== const signUpRequest = () => ({type: actions.FETCH_SIGNUP_REQUEST}); -const signUpSuccess = user => ({type: actions.FETCH_SIGNUP_SUCCESS, user}); -const signUpFailure = error => ({type: actions.FETCH_SIGNUP_FAILURE, error}); +const signUpSuccess = (user) => ({type: actions.FETCH_SIGNUP_SUCCESS, user}); +const signUpFailure = (error) => ({type: actions.FETCH_SIGNUP_FAILURE, error}); -export const fetchSignUp = (formData, redirectUri) => dispatch => { +export const fetchSignUp = (formData, redirectUri) => (dispatch) => { dispatch(signUpRequest()); coralApi('/users', { @@ -230,7 +230,7 @@ export const fetchSignUp = (formData, redirectUri) => dispatch => { .then(({user}) => { dispatch(signUpSuccess(user)); }) - .catch(error => { + .catch((error) => { let errorMessage = lang.t(`error.${error.message}`); // if there is no translation defined, just show the error string @@ -257,7 +257,7 @@ const forgotPasswordFailure = () => ({ type: actions.FETCH_FORGOT_PASSWORD_FAILURE }); -export const fetchForgotPassword = email => dispatch => { +export const fetchForgotPassword = (email) => (dispatch) => { dispatch(forgotPasswordRequest(email)); const redirectUri = pym.parentUrl || location.href; coralApi('/account/password/reset', { @@ -265,16 +265,16 @@ export const fetchForgotPassword = email => dispatch => { body: {email, loc: redirectUri} }) .then(() => dispatch(forgotPasswordSuccess())) - .catch(error => dispatch(forgotPasswordFailure(error))); + .catch((error) => dispatch(forgotPasswordFailure(error))); }; //============================================================================== // LOGOUT //============================================================================== -export const logout = () => dispatch => { +export const logout = () => (dispatch) => { return coralApi('/auth', {method: 'DELETE'}).then(() => { - if (!browser || browser.name !== 'safari') { + if (!browser || browser.name !== 'Safari') { Storage.removeItem('token'); } dispatch({type: actions.LOGOUT}); @@ -286,7 +286,7 @@ export const logout = () => dispatch => { //============================================================================== const checkLoginRequest = () => ({type: actions.CHECK_LOGIN_REQUEST}); -const checkLoginFailure = error => ({type: actions.CHECK_LOGIN_FAILURE, error}); +const checkLoginFailure = (error) => ({type: actions.CHECK_LOGIN_FAILURE, error}); const checkLoginSuccess = (user, isAdmin) => ({ type: actions.CHECK_LOGIN_SUCCESS, @@ -294,12 +294,12 @@ const checkLoginSuccess = (user, isAdmin) => ({ isAdmin }); -export const checkLogin = () => dispatch => { +export const checkLogin = () => (dispatch) => { dispatch(checkLoginRequest()); coralApi('/auth') - .then(result => { + .then((result) => { if (!result.user) { - if (!browser || browser.name !== 'safari') { + if (!browser || browser.name !== 'Safari') { Storage.removeItem('token'); } throw new Error('Not logged in'); @@ -307,14 +307,14 @@ export const checkLogin = () => dispatch => { dispatch(checkLoginSuccess(result.user)); }) - .catch(error => { + .catch((error) => { console.error(error); dispatch(checkLoginFailure(`${error.translation_key}`)); }); }; export const validForm = () => ({type: actions.VALID_FORM}); -export const invalidForm = error => ({type: actions.INVALID_FORM, error}); +export const invalidForm = (error) => ({type: actions.INVALID_FORM, error}); //============================================================================== // VERIFY EMAIL @@ -332,7 +332,7 @@ const verifyEmailFailure = () => ({ type: actions.VERIFY_EMAIL_FAILURE }); -export const requestConfirmEmail = (email, redirectUri) => dispatch => { +export const requestConfirmEmail = (email, redirectUri) => (dispatch) => { dispatch(verifyEmailRequest()); return coralApi('/users/resend-verify', { method: 'POST', @@ -342,7 +342,8 @@ export const requestConfirmEmail = (email, redirectUri) => dispatch => { .then(() => { dispatch(verifyEmailSuccess()); }) - .catch(err => { + .catch((err) => { + // email might have already been verifyed dispatch(verifyEmailFailure(err)); }); diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/response.js index b6e48abb3..ce9de6942 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/response.js @@ -32,9 +32,9 @@ const buildOptions = (inputOptions = {}) => { return options; }; -const handleResp = res => { +const handleResp = (res) => { if (res.status > 399) { - return res.json().then(err => { + return res.json().then((err) => { let message = err.message || res.status; const error = new Error(); diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js index 90fe31b1e..e0e39adf9 100644 --- a/routes/api/auth/index.js +++ b/routes/api/auth/index.js @@ -1,16 +1,9 @@ const express = require('express'); +const bowser = require('bowser'); const {passport, HandleGenerateCredentials, HandleLogout} = require('../../../services/passport'); const router = express.Router(); -const lookup = (i) => { - switch (i) { - case 0: return 'header'; - case 1: return 'cookie'; - case 2: return 'query'; - } -}; - /** * This returns the user if they are logged in. */ @@ -23,22 +16,39 @@ router.get('/', (req, res, next) => { res.status(204).end(); }, (req, res) => { - const authorizations = [ - req.headers.authorization, - req.cookies ? req.cookies.authorization : [], - req.query.authorization - ]; + // If the user is Safari, let's send a cookie + const browser = bowser._detect(req.headers['user-agent']); - res.set('Cache-Control', 'no-cache, no-store, must-revalidate'); + if (browser.name === 'Safari') { - let i = authorizations.findIndex((source) => source !== null && typeof source != 'undefined' && source.length > 0); - if (i >= 0) { - let authorization = authorizations[i]; - let source = lookup(i); + const lookup = (i) => { + switch (i) { + case 0: return 'header'; + case 1: return 'cookie'; + case 2: return 'query'; + } + }; - // Send back the user object. - res.json({authorization, source, user: req.user}); + const authorizations = [ + req.headers.authorization, + req.cookies ? req.cookies.authorization : [], + req.query.authorization + ]; + + res.set('Cache-Control', 'no-cache, no-store, must-revalidate'); + + let i = authorizations.findIndex((source) => source !== null && typeof source != 'undefined' && source.length > 0); + if (i >= 0) { + let authorization = authorizations[i]; + let source = lookup(i); + + // Send back the user object. + res.json({authorization, source, user: req.user}); + } } + + // Send back the user object. + res.json({user: req.user}); }); /** diff --git a/services/passport.js b/services/passport.js index 5e993be51..4b1c10158 100644 --- a/services/passport.js +++ b/services/passport.js @@ -9,6 +9,7 @@ const errors = require('../errors'); const uuid = require('uuid'); const debug = require('debug')('talk:passport'); const {createClient} = require('./redis'); +const bowser = require('bowser'); // Create a redis client to use for authentication. const client = createClient(); @@ -47,11 +48,15 @@ const HandleGenerateCredentials = (req, res, next) => (err, user) => { // Generate the token to re-issue to the frontend. const token = GenerateToken(user); - // handling cookie - res.cookie('authorization', token, { - httpOnly: true, - expires: new Date(Date.now() + 900000) - }); + // If the user is Safari, let's send a cookie + const browser = bowser._detect(req.headers['user-agent']); + + if (browser.name === 'Safari') { + res.cookie('authorization', token, { + httpOnly: true, + expires: new Date(Date.now() + 900000) + }); + } // Send back the details! res.json({user, token}); From 460f3f292b3e89967dd2568b96b4bcc5c4a1f271 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Thu, 25 May 2017 17:41:35 -0300 Subject: [PATCH 04/18] =?UTF-8?q?=C3=81dding=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 4 ++++ package.json | 3 +++ yarn.lock | 40 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 16840f965..d0f0f316b 100644 --- a/app.js +++ b/app.js @@ -11,6 +11,8 @@ const errors = require('./errors'); const {createGraphOptions} = require('./graph'); const apollo = require('graphql-server-express'); const accepts = require('accepts'); +const compression = require('compression'); +const cookieParser = require('cookie-parser'); const app = express(); @@ -31,6 +33,8 @@ app.set('trust proxy', 1); app.use(helmet({ frameguard: false })); +app.use(compression()); +app.use(cookieParser()); app.use(bodyParser.json()); //============================================================================== diff --git a/package.json b/package.json index cbf514cf5..b1eb180ed 100644 --- a/package.json +++ b/package.json @@ -54,10 +54,13 @@ "app-module-path": "^2.2.0", "bcrypt": "^1.0.2", "body-parser": "^1.17.1", + "bowser": "^1.7.0", "cli-table": "^0.3.1", "colors": "^1.1.2", "commander": "^2.9.0", + "compression": "^1.6.2", "connect-redis": "^3.1.0", + "cookie-parser": "^1.4.3", "cross-spawn": "^5.1.0", "csurf": "^1.9.0", "dataloader": "^1.3.0", diff --git a/yarn.lock b/yarn.lock index 3fd71e907..cc1ddd018 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1262,6 +1262,10 @@ boom@2.x.x: dependencies: hoek "2.x.x" +bowser@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.0.tgz#169de4018711f994242bff9a8009e77a1f35e003" + brace-expansion@^1.0.0: version "1.1.7" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" @@ -1395,6 +1399,10 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" +bytes@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.3.0.tgz#d5b680a165b6201739acb611542aabc2d8ceb070" + bytes@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" @@ -1815,6 +1823,12 @@ component-emitter@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" +compressible@~2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.10.tgz#feda1c7f7617912732b29bf8cf26252a20b9eecd" + dependencies: + mime-db ">= 1.27.0 < 2" + compression-webpack-plugin@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-0.4.0.tgz#811de04215f811ea6a12d4d8aed8457d758f13ac" @@ -1824,6 +1838,17 @@ compression-webpack-plugin@^0.4.0: optionalDependencies: node-zopfli "^2.0.0" +compression@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.6.2.tgz#cceb121ecc9d09c52d7ad0c3350ea93ddd402bc3" + dependencies: + accepts "~1.3.3" + bytes "2.3.0" + compressible "~2.0.8" + debug "~2.2.0" + on-headers "~1.0.1" + vary "~1.1.0" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1925,6 +1950,13 @@ convert-source-map@^1.1.0: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" +cookie-parser@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.3.tgz#0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5" + dependencies: + cookie "0.3.1" + cookie-signature "1.0.6" + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -5147,14 +5179,14 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" +"mime-db@>= 1.27.0 < 2", mime-db@~1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" + mime-db@~1.12.0: version "1.12.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.12.0.tgz#3d0c63180f458eb10d325aaa37d7c58ae312e9d7" -mime-db@~1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" - mime-types@^2.1.10, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: version "2.1.15" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" From cbf7cc67f4100ba123eacf7035808da9315079bd Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Thu, 25 May 2017 18:30:01 -0300 Subject: [PATCH 05/18] Extracting token from cookie if safari --- client/coral-framework/actions/auth.js | 10 ++++---- routes/api/auth/index.js | 2 +- services/passport.js | 32 +++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 2f0a2dc0f..f0827ca73 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -112,10 +112,8 @@ const signInFailure = (error) => ({ //============================================================================== export const handleAuthToken = (token) => (dispatch) => { - if (!browser || browser.name !== 'Safari') { - Storage.setItem('exp', jwtDecode(token).exp); - Storage.setItem('token', token); - } + Storage.setItem('exp', jwtDecode(token).exp); + Storage.setItem('token', token); dispatch({type: 'HANDLE_AUTH_TOKEN'}); }; @@ -129,7 +127,9 @@ export const fetchSignIn = (formData) => { return coralApi('/auth/local', {method: 'POST', body: formData}) .then(({token}) => { - dispatch(handleAuthToken(token)); + if (!browser || browser.name !== 'Safari') { + dispatch(handleAuthToken(token)); + } dispatch(hideSignInDialog()); }) .catch((error) => { diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js index e0e39adf9..c5c2e3dfb 100644 --- a/routes/api/auth/index.js +++ b/routes/api/auth/index.js @@ -8,7 +8,7 @@ const router = express.Router(); * This returns the user if they are logged in. */ router.get('/', (req, res, next) => { - + console.log('is there req user>', req.user); if (req.user) { return next(); } diff --git a/services/passport.js b/services/passport.js index 4b1c10158..07a98f789 100644 --- a/services/passport.js +++ b/services/passport.js @@ -174,7 +174,37 @@ const ExtractJwt = require('passport-jwt').ExtractJwt; passport.use(new JwtStrategy({ // Prepare the extractor from the header. - jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'), + jwtFromRequest: (req, res) => { + + const browser = bowser._detect(req.headers['user-agent']); + + if (browser.name === 'Safari') { + const lookup = (i) => { + switch (i) { + case 0: return 'header'; + case 1: return 'cookie'; + case 2: return 'query'; + } + } + + // Adding custom extractor + const authorizations = [ + req.headers.authorization, + req.cookies.authorization, + req.query.authorization + ]; + + let i = authorizations.findIndex((source) => source !== null && typeof source != 'undefined' && source.length > 0); + + if (i >= 0) { + let authorization = authorizations[i]; + let source = lookup(i); + return authorization; + } + } else { + return ExtractJwt.fromAuthHeaderWithScheme('Bearer')(req) + } +}, // Use the secret passed in which is loaded from the environment. This can be // a certificate (loaded) or a HMAC key. From 8171cb53e1301027dfd5d959e4ad13dd486ad3a9 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Thu, 25 May 2017 20:04:32 -0300 Subject: [PATCH 06/18] =?UTF-8?q?=C3=81dding=20another=20extractor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/coral-framework/helpers/response.js | 4 +- client/coral-framework/services/transport.js | 7 ++- routes/api/auth/index.js | 34 +-------------- services/passport.js | 46 +++++++------------- 4 files changed, 25 insertions(+), 66 deletions(-) diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/response.js index ce9de6942..b3e167d16 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/response.js @@ -1,4 +1,4 @@ -import browser from 'detect-browser'; +import browser from 'bowser'; const buildOptions = (inputOptions = {}) => { const defaultOptions = { @@ -15,7 +15,7 @@ const buildOptions = (inputOptions = {}) => { ...inputOptions }; - if (!browser || browser.name !== 'safari') { + if (!browser || browser.name !== 'Safari') { let authorization = localStorage.getItem('token'); if (authorization) { diff --git a/client/coral-framework/services/transport.js b/client/coral-framework/services/transport.js index e421ca3da..8aa33e2f4 100644 --- a/client/coral-framework/services/transport.js +++ b/client/coral-framework/services/transport.js @@ -1,5 +1,6 @@ import {createNetworkInterface} from 'apollo-client'; import * as Storage from '../helpers/storage'; +import browser from 'bowser'; //============================================================================== // NETWORK INTERFACE @@ -21,7 +22,11 @@ networkInterface.use([{ if (!req.options.headers) { req.options.headers = {}; // Create the header object if needed. } - req.options.headers['authorization'] = `Bearer ${Storage.getItem('token')}`; + + if (!browser || browser.name !== 'Safari') { + req.options.headers['authorization'] = `Bearer ${Storage.getItem('token')}`; + } + next(); } }]); diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js index c5c2e3dfb..05fb8b5e4 100644 --- a/routes/api/auth/index.js +++ b/routes/api/auth/index.js @@ -8,7 +8,8 @@ const router = express.Router(); * This returns the user if they are logged in. */ router.get('/', (req, res, next) => { - console.log('is there req user>', req.user); + console.log('REQ.USER', req.user); + if (req.user) { return next(); } @@ -16,37 +17,6 @@ router.get('/', (req, res, next) => { res.status(204).end(); }, (req, res) => { - // If the user is Safari, let's send a cookie - const browser = bowser._detect(req.headers['user-agent']); - - if (browser.name === 'Safari') { - - const lookup = (i) => { - switch (i) { - case 0: return 'header'; - case 1: return 'cookie'; - case 2: return 'query'; - } - }; - - const authorizations = [ - req.headers.authorization, - req.cookies ? req.cookies.authorization : [], - req.query.authorization - ]; - - res.set('Cache-Control', 'no-cache, no-store, must-revalidate'); - - let i = authorizations.findIndex((source) => source !== null && typeof source != 'undefined' && source.length > 0); - if (i >= 0) { - let authorization = authorizations[i]; - let source = lookup(i); - - // Send back the user object. - res.json({authorization, source, user: req.user}); - } - } - // Send back the user object. res.json({user: req.user}); }); diff --git a/services/passport.js b/services/passport.js index 07a98f789..d67407b3c 100644 --- a/services/passport.js +++ b/services/passport.js @@ -170,41 +170,24 @@ const CheckBlacklisted = (jwt) => new Promise((resolve, reject) => { const JwtStrategy = require('passport-jwt').Strategy; const ExtractJwt = require('passport-jwt').ExtractJwt; +let cookieExtractor = function(req) { + let token = null; + + if (req && req.cookies) { + token = req.cookies['authorization']; + } + + return token; +}; + // Extract the JWT from the 'Authorization' header with the 'Bearer' scheme. passport.use(new JwtStrategy({ // Prepare the extractor from the header. - jwtFromRequest: (req, res) => { - - const browser = bowser._detect(req.headers['user-agent']); - - if (browser.name === 'Safari') { - const lookup = (i) => { - switch (i) { - case 0: return 'header'; - case 1: return 'cookie'; - case 2: return 'query'; - } - } - - // Adding custom extractor - const authorizations = [ - req.headers.authorization, - req.cookies.authorization, - req.query.authorization - ]; - - let i = authorizations.findIndex((source) => source !== null && typeof source != 'undefined' && source.length > 0); - - if (i >= 0) { - let authorization = authorizations[i]; - let source = lookup(i); - return authorization; - } - } else { - return ExtractJwt.fromAuthHeaderWithScheme('Bearer')(req) - } -}, + jwtFromRequest: ExtractJwt.fromExtractors([ + cookieExtractor, + ExtractJwt.fromAuthHeaderWithScheme('Bearer') + ]), // Use the secret passed in which is loaded from the environment. This can be // a certificate (loaded) or a HMAC key. @@ -227,6 +210,7 @@ passport.use(new JwtStrategy({ // Load the user from the environment, because we just got a user from the // header. try { + console.log(req.cookies, req.headers) // Check to see if the token has been revoked await CheckBlacklisted(jwt); From 8edc16ed8507a5b2625c2d685f8d8fa28cd2ed32 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Thu, 25 May 2017 20:06:49 -0300 Subject: [PATCH 07/18] Removing logs --- routes/api/auth/index.js | 1 - services/passport.js | 1 - 2 files changed, 2 deletions(-) diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js index 05fb8b5e4..9035ce268 100644 --- a/routes/api/auth/index.js +++ b/routes/api/auth/index.js @@ -8,7 +8,6 @@ const router = express.Router(); * This returns the user if they are logged in. */ router.get('/', (req, res, next) => { - console.log('REQ.USER', req.user); if (req.user) { return next(); diff --git a/services/passport.js b/services/passport.js index d67407b3c..719086f89 100644 --- a/services/passport.js +++ b/services/passport.js @@ -210,7 +210,6 @@ passport.use(new JwtStrategy({ // Load the user from the environment, because we just got a user from the // header. try { - console.log(req.cookies, req.headers) // Check to see if the token has been revoked await CheckBlacklisted(jwt); From 276e58060aee26f6213b52c7c99abe34030ddb89 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 26 May 2017 17:11:29 +0700 Subject: [PATCH 08/18] Fix change username dialog --- .../coral-plugin-auth/client/components/ChangeUsername.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/coral-plugin-auth/client/components/ChangeUsername.js b/plugins/coral-plugin-auth/client/components/ChangeUsername.js index 9b0cbb1a6..5b5a54510 100644 --- a/plugins/coral-plugin-auth/client/components/ChangeUsername.js +++ b/plugins/coral-plugin-auth/client/components/ChangeUsername.js @@ -23,7 +23,7 @@ class ChangeUsernameContainer extends React.Component { this.state = { formData: { - username: props.user.username + username: (props.auth.user && props.auth.user.username) || '' }, errors: {}, showErrors: false @@ -84,7 +84,7 @@ class ChangeUsernameContainer extends React.Component { const {validForm, invalidForm} = this.props; this.displayErrors(); if (this.isCompleted() && !Object.keys(errors).length) { - this.props.createUsername(this.props.user.id, this.state.formData); + this.props.createUsername(this.props.auth.user.id, this.state.formData); validForm(); } else { invalidForm(lang.t('createdisplay.checkTheForm')); @@ -113,7 +113,9 @@ class ChangeUsernameContainer extends React.Component { } } -const mapStateToProps = ({auth, user}) => ({auth, user}); +const mapStateToProps = ({auth}) => ({ + auth: auth.toJS() +}); const mapDispatchToProps = dispatch => bindActionCreators( From bab47c9d444c450a98ab8d5ab6a77ee941453721 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 26 May 2017 20:33:43 +0700 Subject: [PATCH 09/18] Separate store --- client/coral-embed-stream/src/index.js | 6 ++---- client/coral-framework/actions/auth.js | 11 ++++++----- .../client/components/ChangeUsername.js | 2 +- routes/api/auth/index.js | 1 - 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/client/coral-embed-stream/src/index.js b/client/coral-embed-stream/src/index.js index 0306079f3..66ed9ad56 100644 --- a/client/coral-embed-stream/src/index.js +++ b/client/coral-embed-stream/src/index.js @@ -8,16 +8,14 @@ import './graphql'; import {addExternalConfig} from 'coral-embed-stream/src/actions/config'; import reducers from './reducers'; -import localStore, {injectReducers} from 'coral-framework/services/store'; +import store, {injectReducers} from 'coral-framework/services/store'; import AppRouter from './AppRouter'; import {pym} from 'coral-framework'; injectReducers(reducers); -const store = (window.opener && window.opener.coralStore) ? window.opener.coralStore : localStore; - // Don't run this in the popup. -if (store === localStore) { +if (!window.opener) { store.dispatch(checkLogin()); pym.sendMessage('getConfig'); diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index f0827ca73..c4d6d782e 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -192,7 +192,7 @@ export const fetchSignUpFacebook = () => (dispatch) => { ); }; -export const facebookCallback = (err, data) => (dispatch, getState) => { +export const facebookCallback = (err, data) => (dispatch) => { if (err) { dispatch(signInFacebookFailure(err)); return; @@ -201,10 +201,6 @@ export const facebookCallback = (err, data) => (dispatch, getState) => { dispatch(handleAuthToken(data.token)); dispatch(signInFacebookSuccess(data.user)); dispatch(hideSignInDialog()); - const {user: {canEditName, status}} = getState().auth.toJS(); - if (canEditName && status !== 'BANNED') { - dispatch(showCreateUsernameDialog()); - } } catch (err) { dispatch(signInFacebookFailure(err)); return; @@ -306,6 +302,11 @@ export const checkLogin = () => (dispatch) => { } dispatch(checkLoginSuccess(result.user)); + + // Display create username dialog if necessary. + if (result.user.canEditName && result.user.status !== 'BANNED') { + dispatch(showCreateUsernameDialog()); + } }) .catch((error) => { console.error(error); diff --git a/plugins/coral-plugin-auth/client/components/ChangeUsername.js b/plugins/coral-plugin-auth/client/components/ChangeUsername.js index 5b5a54510..761fe786d 100644 --- a/plugins/coral-plugin-auth/client/components/ChangeUsername.js +++ b/plugins/coral-plugin-auth/client/components/ChangeUsername.js @@ -100,7 +100,7 @@ class ChangeUsernameContainer extends React.Component { return (
Date: Fri, 26 May 2017 20:48:24 +0700 Subject: [PATCH 10/18] Detect mobile safari & desktop safari --- client/coral-admin/src/actions/auth.js | 6 +++--- client/coral-framework/actions/auth.js | 8 ++++---- client/coral-framework/helpers/response.js | 4 ++-- client/coral-framework/services/transport.js | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 16b6c774a..69e7d4303 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,4 +1,4 @@ -import browser from 'bowser'; +import bowser from 'bowser'; import * as actions from '../constants/auth'; import coralApi from 'coral-framework/helpers/response'; import * as Storage from 'coral-framework/helpers/storage'; @@ -29,7 +29,7 @@ export const handleLogin = (email, password, recaptchaResponse) => (dispatch) => .then(({user, token}) => { if (!user) { - if (!browser || browser.name !== 'Safari') { + if (!bowser.safari && !bowser.ios) { Storage.removeItem('token'); } return dispatch(checkLoginFailure('not logged in')); @@ -97,7 +97,7 @@ export const checkLogin = () => (dispatch) => { return coralApi('/auth') .then(({user}) => { if (!user) { - if (!browser || browser.name !== 'Safari') { + if (!bowser.safari && !bowser.ios) { Storage.removeItem('token'); } return dispatch(checkLoginFailure('not logged in')); diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index c4d6d782e..2deda5f1d 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -1,6 +1,6 @@ import jwtDecode from 'jwt-decode'; import {pym} from 'coral-framework'; -import browser from 'bowser'; +import bowser from 'bowser'; import * as actions from '../constants/auth'; import * as Storage from '../helpers/storage'; import coralApi, {base} from '../helpers/response'; @@ -127,7 +127,7 @@ export const fetchSignIn = (formData) => { return coralApi('/auth/local', {method: 'POST', body: formData}) .then(({token}) => { - if (!browser || browser.name !== 'Safari') { + if (!bowser.safari && !bowser.ios) { dispatch(handleAuthToken(token)); } dispatch(hideSignInDialog()); @@ -270,7 +270,7 @@ export const fetchForgotPassword = (email) => (dispatch) => { export const logout = () => (dispatch) => { return coralApi('/auth', {method: 'DELETE'}).then(() => { - if (!browser || browser.name !== 'Safari') { + if (!bowser.safari && !bowser.ios) { Storage.removeItem('token'); } dispatch({type: actions.LOGOUT}); @@ -295,7 +295,7 @@ export const checkLogin = () => (dispatch) => { coralApi('/auth') .then((result) => { if (!result.user) { - if (!browser || browser.name !== 'Safari') { + if (!bowser.safari && !bowser.ios) { Storage.removeItem('token'); } throw new Error('Not logged in'); diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/response.js index b3e167d16..ac6e53cf9 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/response.js @@ -1,4 +1,4 @@ -import browser from 'bowser'; +import bowser from 'bowser'; const buildOptions = (inputOptions = {}) => { const defaultOptions = { @@ -15,7 +15,7 @@ const buildOptions = (inputOptions = {}) => { ...inputOptions }; - if (!browser || browser.name !== 'Safari') { + if (!bowser.safari && !bowser.ios) { let authorization = localStorage.getItem('token'); if (authorization) { diff --git a/client/coral-framework/services/transport.js b/client/coral-framework/services/transport.js index 8aa33e2f4..0c430a021 100644 --- a/client/coral-framework/services/transport.js +++ b/client/coral-framework/services/transport.js @@ -1,6 +1,6 @@ import {createNetworkInterface} from 'apollo-client'; import * as Storage from '../helpers/storage'; -import browser from 'bowser'; +import bowser from 'bowser'; //============================================================================== // NETWORK INTERFACE @@ -23,7 +23,7 @@ networkInterface.use([{ req.options.headers = {}; // Create the header object if needed. } - if (!browser || browser.name !== 'Safari') { + if (!bowser.safari && !bowser.ios) { req.options.headers['authorization'] = `Bearer ${Storage.getItem('token')}`; } From 6a9f216c0f98dd404e744d8c75e6ed6f5390acd8 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 26 May 2017 20:58:48 +0700 Subject: [PATCH 11/18] Detect mobile & desktop safari server side --- services/passport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/passport.js b/services/passport.js index 719086f89..f3eb0da83 100644 --- a/services/passport.js +++ b/services/passport.js @@ -51,7 +51,7 @@ const HandleGenerateCredentials = (req, res, next) => (err, user) => { // If the user is Safari, let's send a cookie const browser = bowser._detect(req.headers['user-agent']); - if (browser.name === 'Safari') { + if (browser.ios || browser.safari) { res.cookie('authorization', token, { httpOnly: true, expires: new Date(Date.now() + 900000) From d4569b5f518d5e029cd1f0ffdc4f3d40bed58303 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 26 May 2017 21:37:11 +0700 Subject: [PATCH 12/18] Set cookie for facebook login on safari --- services/passport.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/services/passport.js b/services/passport.js index f3eb0da83..4f5a8fb8d 100644 --- a/services/passport.js +++ b/services/passport.js @@ -33,6 +33,17 @@ const GenerateToken = (user) => JWT.sign({}, JWT_SECRET, { audience: JWT_AUDIENCE }); +// SetTokenForSafari sends the token in a cookie for Safari clients. +const SetTokenForSafari = (req, res, token) => { + const browser = bowser._detect(req.headers['user-agent']); + if (browser.ios || browser.safari) { + res.cookie('authorization', token, { + httpOnly: true, + expires: new Date(Date.now() + 900000) + }); + } +}; + // HandleGenerateCredentials validates that an authentication scheme did indeed // return a user, if it did, then sign and return the user and token to be used // by the frontend to display and update the UI. @@ -48,15 +59,7 @@ const HandleGenerateCredentials = (req, res, next) => (err, user) => { // Generate the token to re-issue to the frontend. const token = GenerateToken(user); - // If the user is Safari, let's send a cookie - const browser = bowser._detect(req.headers['user-agent']); - - if (browser.ios || browser.safari) { - res.cookie('authorization', token, { - httpOnly: true, - expires: new Date(Date.now() + 900000) - }); - } + SetTokenForSafari(req, res, token); // Send back the details! res.json({user, token}); @@ -77,6 +80,8 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => { // Generate the token to re-issue to the frontend. const token = GenerateToken(user); + SetTokenForSafari(req, res, token); + // We logged in the user! Let's send back the user data. res.render('auth-callback', {auth: JSON.stringify({err: null, data: {user, token}})}); }; From 7b6b46f3e5551433683ec972832d8f438dafb07b Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 26 May 2017 21:38:29 +0700 Subject: [PATCH 13/18] Sync username with store --- .../client/components/ChangeUsername.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/coral-plugin-auth/client/components/ChangeUsername.js b/plugins/coral-plugin-auth/client/components/ChangeUsername.js index 761fe786d..cbb7adba5 100644 --- a/plugins/coral-plugin-auth/client/components/ChangeUsername.js +++ b/plugins/coral-plugin-auth/client/components/ChangeUsername.js @@ -30,6 +30,16 @@ class ChangeUsernameContainer extends React.Component { }; } + componentWillReceiveProps(next) { + if (!this.props.auth.showCreateUsernameDialog && next.auth.showCreateUsernameDialog) { + this.setState({ + formData: { + username: (this.props.auth.user && this.props.auth.user.username) || '', + }, + }); + } + } + handleChange = e => { const {name, value} = e.target; this.setState( From 99a9154d4053a607909b613a040e8a714e901bf9 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Sat, 27 May 2017 00:26:04 +0700 Subject: [PATCH 14/18] Remove unused dependency --- package.json | 1 - yarn.lock | 4 ---- 2 files changed, 5 deletions(-) diff --git a/package.json b/package.json index b1eb180ed..11634fa8d 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "csurf": "^1.9.0", "dataloader": "^1.3.0", "debug": "^2.6.3", - "detect-browser": "^1.7.0", "dotenv": "^4.0.0", "ejs": "^2.5.6", "env-rewrite": "^1.0.2", diff --git a/yarn.lock b/yarn.lock index cc1ddd018..3d01f098e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2422,10 +2422,6 @@ destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" -detect-browser@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-1.7.0.tgz#11758cd6aa07d76c25784036d19154ae0392c3b3" - detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" From a7cdcb0bf7f5d6d8918da9c71317886b23e7f3f9 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Sat, 27 May 2017 00:54:36 +0700 Subject: [PATCH 15/18] Fix request options and use better filename --- client/coral-admin/src/actions/assets.js | 2 +- client/coral-admin/src/actions/auth.js | 2 +- client/coral-admin/src/actions/community.js | 2 +- client/coral-admin/src/actions/install.js | 2 +- client/coral-admin/src/actions/settings.js | 2 +- client/coral-admin/src/actions/users.js | 2 +- client/coral-framework/actions/asset.js | 2 +- client/coral-framework/actions/auth.js | 2 +- client/coral-framework/actions/user.js | 2 +- .../helpers/{response.js => request.js} | 13 +++++-------- 10 files changed, 14 insertions(+), 17 deletions(-) rename client/coral-framework/helpers/{response.js => request.js} (82%) diff --git a/client/coral-admin/src/actions/assets.js b/client/coral-admin/src/actions/assets.js index f5c2f848a..8a18a8e3f 100644 --- a/client/coral-admin/src/actions/assets.js +++ b/client/coral-admin/src/actions/assets.js @@ -8,7 +8,7 @@ import { UPDATE_ASSETS } from '../constants/assets'; -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; /** * Action disptacher related to assets diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 69e7d4303..57871b79d 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,6 +1,6 @@ import bowser from 'bowser'; import * as actions from '../constants/auth'; -import coralApi from 'coral-framework/helpers/response'; +import coralApi from 'coral-framework/helpers/request'; import * as Storage from 'coral-framework/helpers/storage'; import {handleAuthToken} from 'coral-framework/actions/auth'; diff --git a/client/coral-admin/src/actions/community.js b/client/coral-admin/src/actions/community.js index 9a8743762..36b9ffb0a 100644 --- a/client/coral-admin/src/actions/community.js +++ b/client/coral-admin/src/actions/community.js @@ -14,7 +14,7 @@ import { HIDE_SUSPENDUSER_DIALOG } from '../constants/community'; -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; export const fetchAccounts = (query = {}) => (dispatch) => { diff --git a/client/coral-admin/src/actions/install.js b/client/coral-admin/src/actions/install.js index 21ff58de1..15413e220 100644 --- a/client/coral-admin/src/actions/install.js +++ b/client/coral-admin/src/actions/install.js @@ -1,4 +1,4 @@ -import coralApi from 'coral-framework/helpers/response'; +import coralApi from 'coral-framework/helpers/request'; import * as actions from '../constants/install'; import validate from 'coral-framework/helpers/validate'; import errorMsj from 'coral-framework/helpers/error'; diff --git a/client/coral-admin/src/actions/settings.js b/client/coral-admin/src/actions/settings.js index b0530e77e..65c200420 100644 --- a/client/coral-admin/src/actions/settings.js +++ b/client/coral-admin/src/actions/settings.js @@ -1,4 +1,4 @@ -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; export const SETTINGS_LOADING = 'SETTINGS_LOADING'; export const SETTINGS_RECEIVED = 'SETTINGS_RECEIVED'; diff --git a/client/coral-admin/src/actions/users.js b/client/coral-admin/src/actions/users.js index 2642db90d..e888d5b74 100644 --- a/client/coral-admin/src/actions/users.js +++ b/client/coral-admin/src/actions/users.js @@ -1,4 +1,4 @@ -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; import * as userTypes from '../constants/users'; /** diff --git a/client/coral-framework/actions/asset.js b/client/coral-framework/actions/asset.js index 129cd1c3c..9c0081470 100644 --- a/client/coral-framework/actions/asset.js +++ b/client/coral-framework/actions/asset.js @@ -1,5 +1,5 @@ import * as actions from '../constants/asset'; -import coralApi from '../helpers/response'; +import coralApi from '../helpers/request'; import {addNotification} from '../actions/notification'; import I18n from 'coral-framework/modules/i18n/i18n'; diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 2deda5f1d..f714f1fac 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -3,7 +3,7 @@ import {pym} from 'coral-framework'; import bowser from 'bowser'; import * as actions from '../constants/auth'; import * as Storage from '../helpers/storage'; -import coralApi, {base} from '../helpers/response'; +import coralApi, {base} from '../helpers/request'; const lang = new I18n(translations); import translations from './../translations'; diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index 910fe0865..95d4116a6 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -1,5 +1,5 @@ import {addNotification} from '../actions/notification'; -import coralApi from '../helpers/response'; +import coralApi from '../helpers/request'; import * as actions from '../constants/auth'; import I18n from 'coral-framework/modules/i18n/i18n'; diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/request.js similarity index 82% rename from client/coral-framework/helpers/response.js rename to client/coral-framework/helpers/request.js index ac6e53cf9..0e15003e1 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/request.js @@ -1,4 +1,6 @@ import bowser from 'bowser'; +import * as Storage from './storage'; +import merge from 'lodash/merge'; const buildOptions = (inputOptions = {}) => { const defaultOptions = { @@ -10,18 +12,13 @@ const buildOptions = (inputOptions = {}) => { credentials: 'same-origin' }; - let options = { - ...defaultOptions, - ...inputOptions - }; + let options = merge({}, defaultOptions, inputOptions); if (!bowser.safari && !bowser.ios) { - let authorization = localStorage.getItem('token'); + let authorization = Storage.getItem('token'); if (authorization) { - options.headers = { - Authorization: `Bearer ${authorization}` - }; + options.headers.Authorization = `Bearer ${authorization}`; } } From 52b3dcab43d0f8c48ac5e542996c5c8ab6069f5a Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Sat, 27 May 2017 01:05:12 +0700 Subject: [PATCH 16/18] Fix changing username not updating --- plugins/coral-plugin-auth/client/components/UserBox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/coral-plugin-auth/client/components/UserBox.js b/plugins/coral-plugin-auth/client/components/UserBox.js index a4a0e1c99..d8adf2380 100644 --- a/plugins/coral-plugin-auth/client/components/UserBox.js +++ b/plugins/coral-plugin-auth/client/components/UserBox.js @@ -25,7 +25,7 @@ const UserBox = ({loggedIn, user, logout, onShowProfile}) => ( const mapStateToProps = ({auth, user}) => ({ loggedIn: auth.toJS().loggedIn, - user: user.toJS() + user: auth.toJS().user }); const mapDispatchToProps = dispatch => From 8713a2ae875493a45f1dfe1068d88baf3eadcfa4 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Sat, 27 May 2017 02:08:54 +0700 Subject: [PATCH 17/18] Enable Linting --- .eslintignore | 5 ++++ .../client/components/ChangeUsername.js | 14 +++++----- .../client/components/FakeComment.js | 3 ++- .../client/components/ForgotContent.js | 4 +-- .../client/components/SignInButton.js | 2 +- .../client/components/SignInContainer.js | 27 ++++++++++--------- .../client/components/SignUpContent.js | 2 +- .../client/components/UserBox.js | 6 ++--- plugins/coral-plugin-auth/client/index.js | 3 ++- plugins/coral-plugin-auth/index.js | 3 ++- .../client/components/LikeButton.js | 17 ++++++------ .../client/containers/LikeButton.js | 27 ++++++++++--------- .../coral-plugin-love/client/LoveButton.js | 2 +- plugins/coral-plugin-love/index.js | 8 +++--- .../coral-plugin-mod/client/components/Box.js | 5 ++-- .../client/components/Container.js | 6 ++--- plugins/coral-plugin-mod/index.js | 4 +-- .../client/components/OffTopicCheckbox.js | 7 +++-- .../client/components/OffTopicTag.js | 4 +-- 19 files changed, 78 insertions(+), 71 deletions(-) diff --git a/.eslintignore b/.eslintignore index fc0d50a25..96e49da53 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,5 +3,10 @@ client/lib **/*.html plugins/* !plugins/coral-plugin-facebook-auth +!plugins/coral-plugin-auth !plugins/coral-plugin-respect +!plugins/coral-plugin-offtopic +!plugins/coral-plugin-like +!plugins/coral-plugin-mod +!plugins/coral-plugin-love node_modules diff --git a/plugins/coral-plugin-auth/client/components/ChangeUsername.js b/plugins/coral-plugin-auth/client/components/ChangeUsername.js index cbb7adba5..ee02b2e17 100644 --- a/plugins/coral-plugin-auth/client/components/ChangeUsername.js +++ b/plugins/coral-plugin-auth/client/components/ChangeUsername.js @@ -40,10 +40,10 @@ class ChangeUsernameContainer extends React.Component { } } - handleChange = e => { + handleChange = (e) => { const {name, value} = e.target; this.setState( - state => ({ + (state) => ({ ...state, formData: { ...state.formData, @@ -57,7 +57,7 @@ class ChangeUsernameContainer extends React.Component { }; addError = (name, error) => { - return this.setState(state => ({ + return this.setState((state) => ({ errors: { ...state.errors, [name]: error @@ -75,20 +75,20 @@ class ChangeUsernameContainer extends React.Component { } else { const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line // Removes Error - this.setState(state => ({...state, errors})); + this.setState((state) => ({...state, errors})); } }; isCompleted = () => { const {formData} = this.state; - return !Object.keys(formData).filter(prop => !formData[prop].length).length; + return !Object.keys(formData).filter((prop) => !formData[prop].length).length; }; displayErrors = (show = true) => { this.setState({showErrors: show}); }; - handleSubmitUsername = e => { + handleSubmitUsername = (e) => { e.preventDefault(); const {errors} = this.state; const {validForm, invalidForm} = this.props; @@ -127,7 +127,7 @@ const mapStateToProps = ({auth}) => ({ auth: auth.toJS() }); -const mapDispatchToProps = dispatch => +const mapDispatchToProps = (dispatch) => bindActionCreators( { createUsername, diff --git a/plugins/coral-plugin-auth/client/components/FakeComment.js b/plugins/coral-plugin-auth/client/components/FakeComment.js index 01cc6863e..349e7d0a4 100644 --- a/plugins/coral-plugin-auth/client/components/FakeComment.js +++ b/plugins/coral-plugin-auth/client/components/FakeComment.js @@ -69,4 +69,5 @@ export const FakeComment = ({username, created_at, body}) => (
-); \ No newline at end of file +); + diff --git a/plugins/coral-plugin-auth/client/components/ForgotContent.js b/plugins/coral-plugin-auth/client/components/ForgotContent.js index 2099330bc..8bd6b27a6 100644 --- a/plugins/coral-plugin-auth/client/components/ForgotContent.js +++ b/plugins/coral-plugin-auth/client/components/ForgotContent.js @@ -7,7 +7,7 @@ import I18n from 'coral-framework/modules/i18n/i18n'; const lang = new I18n(translations); class ForgotContent extends React.Component { - handleSubmit = e => { + handleSubmit = (e) => { e.preventDefault(); this.props.fetchForgotPassword(this.emailInput.value); }; @@ -25,7 +25,7 @@ class ForgotContent extends React.Component {
(this.emailInput = input)} + ref={(input) => (this.emailInput = input)} type="text" style={{fontSize: 16}} id="email" diff --git a/plugins/coral-plugin-auth/client/components/SignInButton.js b/plugins/coral-plugin-auth/client/components/SignInButton.js index b54cbfedd..881233c83 100644 --- a/plugins/coral-plugin-auth/client/components/SignInButton.js +++ b/plugins/coral-plugin-auth/client/components/SignInButton.js @@ -18,7 +18,7 @@ const mapStateToProps = ({auth}) => ({ loggedIn: auth.toJS().loggedIn }); -const mapDispatchToProps = dispatch => +const mapDispatchToProps = (dispatch) => bindActionCreators({showSignInDialog}, dispatch); export default connect(mapStateToProps, mapDispatchToProps)(SignInButton); diff --git a/plugins/coral-plugin-auth/client/components/SignInContainer.js b/plugins/coral-plugin-auth/client/components/SignInContainer.js index cbff000ee..523f343ed 100644 --- a/plugins/coral-plugin-auth/client/components/SignInContainer.js +++ b/plugins/coral-plugin-auth/client/components/SignInContainer.js @@ -61,7 +61,8 @@ class SignInContainer extends React.Component { window.removeEventListener('storage', this.handleAuth); } - handleAuth = e => { + handleAuth = (e) => { + // Listening to FB changes // FB localStorage key is 'auth' const authCallback = this.props.facebookCallback; @@ -72,10 +73,10 @@ class SignInContainer extends React.Component { } }; - handleChange = e => { + handleChange = (e) => { const {name, value} = e.target; this.setState( - state => ({ + (state) => ({ ...state, formData: { ...state.formData, @@ -88,12 +89,12 @@ class SignInContainer extends React.Component { ); }; - handleChangeEmail = e => { + handleChangeEmail = (e) => { const {value} = e.target; this.setState({emailToBeResent: value}); }; - handleResendVerification = e => { + handleResendVerification = (e) => { e.preventDefault(); this.props .requestConfirmEmail( @@ -102,6 +103,7 @@ class SignInContainer extends React.Component { ) .then(() => { setTimeout(() => { + // allow success UI to be shown for a second, and then close the modal this.props.hideSignInDialog(); }, 2500); @@ -109,7 +111,7 @@ class SignInContainer extends React.Component { }; addError = (name, error) => { - return this.setState(state => ({ + return this.setState((state) => ({ errors: { ...state.errors, [name]: error @@ -133,20 +135,20 @@ class SignInContainer extends React.Component { } else { const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line // Removes Error - this.setState(state => ({...state, errors})); + this.setState((state) => ({...state, errors})); } }; isCompleted = () => { const {formData} = this.state; - return !Object.keys(formData).filter(prop => !formData[prop].length).length; + return !Object.keys(formData).filter((prop) => !formData[prop].length).length; }; displayErrors = (show = true) => { this.setState({showErrors: show}); }; - handleSignUp = e => { + handleSignUp = (e) => { e.preventDefault(); const {errors} = this.state; const {fetchSignUp, validForm, invalidForm} = this.props; @@ -159,7 +161,7 @@ class SignInContainer extends React.Component { } }; - handleSignIn = e => { + handleSignIn = (e) => { e.preventDefault(); this.props.fetchSignIn(this.state.formData); }; @@ -183,17 +185,16 @@ class SignInContainer extends React.Component { } } -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ auth: state.auth.toJS() }); -const mapDispatchToProps = dispatch => +const mapDispatchToProps = (dispatch) => bindActionCreators( { checkLogin, facebookCallback, fetchSignUp, - fetchSignUp, fetchSignIn, fetchSignInFacebook, fetchSignUpFacebook, diff --git a/plugins/coral-plugin-auth/client/components/SignUpContent.js b/plugins/coral-plugin-auth/client/components/SignUpContent.js index 5f2dd36ea..fd885d1fe 100644 --- a/plugins/coral-plugin-auth/client/components/SignUpContent.js +++ b/plugins/coral-plugin-auth/client/components/SignUpContent.js @@ -1,5 +1,5 @@ import styles from './styles.css'; -import React, {PropTypes} from 'react'; +import React from 'react'; import translations from '../translations'; import I18n from 'coral-framework/modules/i18n/i18n'; import {Button, TextField, Spinner, Success, Alert} from 'coral-ui'; diff --git a/plugins/coral-plugin-auth/client/components/UserBox.js b/plugins/coral-plugin-auth/client/components/UserBox.js index d8adf2380..cfd0fc9d2 100644 --- a/plugins/coral-plugin-auth/client/components/UserBox.js +++ b/plugins/coral-plugin-auth/client/components/UserBox.js @@ -4,7 +4,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import translations from '../translations'; import I18n from 'coral-framework/modules/i18n/i18n'; -import {showSignInDialog, logout} from 'coral-framework/actions/auth'; +import {logout} from 'coral-framework/actions/auth'; const lang = new I18n(translations); const UserBox = ({loggedIn, user, logout, onShowProfile}) => ( @@ -23,12 +23,12 @@ const UserBox = ({loggedIn, user, logout, onShowProfile}) => (
); -const mapStateToProps = ({auth, user}) => ({ +const mapStateToProps = ({auth}) => ({ loggedIn: auth.toJS().loggedIn, user: auth.toJS().user }); -const mapDispatchToProps = dispatch => +const mapDispatchToProps = (dispatch) => bindActionCreators({logout}, dispatch); export default connect(mapStateToProps, mapDispatchToProps)(UserBox); diff --git a/plugins/coral-plugin-auth/client/index.js b/plugins/coral-plugin-auth/client/index.js index 41fa3adbe..c41c097bd 100644 --- a/plugins/coral-plugin-auth/client/index.js +++ b/plugins/coral-plugin-auth/client/index.js @@ -8,4 +8,5 @@ export default { stream: [UserBox, SignInButton, ChangeUserNameContainer], login: [SignInContainer] } -}; \ No newline at end of file +}; + diff --git a/plugins/coral-plugin-auth/index.js b/plugins/coral-plugin-auth/index.js index a09954537..85dfb349b 100644 --- a/plugins/coral-plugin-auth/index.js +++ b/plugins/coral-plugin-auth/index.js @@ -1 +1,2 @@ -module.exports = {}; \ No newline at end of file +module.exports = {}; + diff --git a/plugins/coral-plugin-like/client/components/LikeButton.js b/plugins/coral-plugin-like/client/components/LikeButton.js index b91ffd42a..0a88d35a4 100644 --- a/plugins/coral-plugin-like/client/components/LikeButton.js +++ b/plugins/coral-plugin-like/client/components/LikeButton.js @@ -1,19 +1,18 @@ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import styles from './style.css'; -import Icon from './Icon'; -import { I18n } from 'coral-framework'; +import {I18n} from 'coral-framework'; import cn from 'classnames'; import translations from '../translations.json'; -import { getMyActionSummary, getTotalActionCount } from 'coral-framework/utils'; +import {getMyActionSummary, getTotalActionCount} from 'coral-framework/utils'; const lang = new I18n(translations); const name = 'coral-plugin-like'; class LikeButton extends Component { handleClick = () => { - const { postLike, showSignInDialog, deleteAction } = this.props; - const { root: { me }, comment } = this.props; + const {postLike, showSignInDialog, deleteAction} = this.props; + const {root: {me}, comment} = this.props; const myLikeActionSummary = getMyActionSummary( 'LikeActionSummary', @@ -42,7 +41,7 @@ class LikeButton extends Component { }; render() { - const { comment } = this.props; + const {comment} = this.props; if (!comment) { return null; @@ -56,7 +55,7 @@ class LikeButton extends Component {